decorator pattern - cs356.yusun.iocs356.yusun.io/slides/l16-decorator.pdf · decorator pattern....

32
CS356 Object-Oriented Design and Programming http://cs356.yusun.io November 7, 2014 Yu Sun, Ph.D. http://yusun.io [email protected] Decorator Pattern

Upload: others

Post on 11-Jul-2020

12 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

CS356 Object-Oriented Design and Programming http://cs356.yusun.io November 7, 2014 Yu Sun, Ph.D. http://yusun.io [email protected]

Decorator Pattern

Page 2: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Decorator

¿  Intent ¿  Dynamically attach additional responsibilities to an object ¿  Provide a flexible alternative to subclassing (static) ¿  Decorating object is transparent to the core component

¿  Also Known As – Wrapper

Page 3: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Motivation

¿  We want to add different kinds of borders and/or scrollbars to a TextView GUI component

¿  Borders – Plain, 3D, or Fancy ¿  Scrollbars – Horizontal and/or Vertical

Page 4: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Motivation

1.  TextView-Plain 2.  TextView-3D 3.  TextView-Fancy 4.  TextView-Horizontal 5.  TextView-Vertical 6.  TextView-Horizontal-Vertical 7.  TextView-Plain-Horizontal 8.  TextView-Plain-Vertical

9.  TextView-Plain-Horizontal-Vertical 10.  TextView-3D-Horizontal 11.  TextView-3D-Vertical 12.  TextView-3D-Horizontal-Vertical 13.  TextView-Fancy-Horizontal 14.  TextView-Fancy-Vertical 15.  TextView-Fancy-Horizontal-Vertical

¿  An inheritance solution requires 15 subclasses to represent each type of view

Page 5: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Solution 1: Use Object Composition

¿  Is it Open-Closed? ¿  Can you add new features without affecting TextView?

¿  e.g., what about adding sound to a TextView?

border scrollbar

TextView

Horizontal Vertical

Scrollbar

Vertical Scrollbar

Horizontal Scrollbar

Border

PlainBorder 3DBorder FancyBorder

Scrollbar

Page 6: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Decorator Pattern Solution

¿  Change the Skin, not the Guts! ¿  TextView has no borders or scrollbars! ¿  Add borders and scrollbars on top of a TextView

Horizontal Vertical

Scrollbar

Vertical Scrollbar

Horizontal Scrollbar

VisualComponent

TextView

component

VisualDecorator

Scrollbar Border

PlainBorder 3DBorder FancyBorder

Page 7: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Structure

Component

ConcreteComponent

Operation()

Decorator

ConcreteDecoratorB ConcreteDecoratorA

Operation()

Operation() AddedBehavior()

Operation()

addedState

component

Decorator::Operation(); AddBehavior();

Component->Operation();

What is significance of this?

The decorator forwards requests to the component and may perform additional actions (such as drawing a border ) be fore or a f ter any forwarding

Page 8: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Component u  Defines the interface for objects that can have responsibilities

added dynamically

Component

ConcreteComponent

Operation()

Decorator

ConcreteDecoratorB ConcreteDecoratorA

Operation()

Operation() AddedBehavior()

Operation()

addedState

component

Decorator::Operation(); AddBehavior();

Component->Operation();

Page 9: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

ConcreteComponent u  The "base" object to which additional responsibilities can be

added

Component

ConcreteComponent

Operation()

Decorator

ConcreteDecoratorB ConcreteDecoratorA

Operation()

Operation() AddedBehavior()

Operation()

addedState

component

Decorator::Operation(); AddBehavior();

Component->Operation();

Page 10: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Decorator u  Maintains a reference to a Component object u  Defines an interface conformant to Component's interface

Component

ConcreteComponent

Operation()

Decorator

ConcreteDecoratorB ConcreteDecoratorA

Operation()

Operation() AddedBehavior()

Operation()

addedState

component

Decorator::Operation(); AddBehavior();

Component->Operation();

Page 11: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

ConcreteDecorator u  Adds responsibilities to the component

Component

ConcreteComponent

Operation()

Decorator

ConcreteDecoratorB ConcreteDecoratorA

Operation()

Operation() AddedBehavior()

Operation()

addedState

component

Decorator::Operation(); AddBehavior();

Component->Operation();

Page 12: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Decorator

¿  Applicability ¿  Dynamically and transparently attach responsibilities to

objects ¿  Responsibilities that can be withdrawn ¿  Extension by subclassing is impractical

¿  May lead to too many subclasses

Page 13: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Example – Sales Ticket Printing

Page 14: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Example: Decorate Sales Ticket Printing

¿  Assume the SalesTicket currently creates an html sales receipt for an Airline Ticket

¿  New Requirements ¿  Add header with company name ¿  Add footer that is an advertisement ¿  During the holidays add holiday relevant header(s) and

footer(s) ¿  We’re not sure how many such things

¿  One solution ¿  Place control in SalesTicket

¿  Then you need flags to control what header(s) get printed

Page 15: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Decorator Approach

¿  A layered approach ¿  Start chain with decorators ¿  End with original object

Decorator 1

Decorator 2

Concrete Component

Page 16: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Example – Sales Ticket Printing

printTicket()

component

TicketDecorator

HeaderDecorator1

HeaderDecorator1(Component) printTicket() printHeader()

Component

TicketDecorator(Component) printTicket()

printTicket() main(String[])

SalesOrder

getSalesTicket()

Configuration

HeaderDecorator2

HeaderDecorator2(Component) printTicket() printHeader()

FooterDecorator1

FooterDecorator1(Component) printTicket() printFooter()

FooterDecorator2

FooterDecorator2(Component) printTicket() printFooter()

SalesTicket

printTicket()

Page 17: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

printTicket()

component

TicketDecorator

HeaderDecorator1

HeaderDecorator1(Component) printTicket() printHeader()

Component

TicketDecorator(Component) printTicket()

printTicket() main(String[])

SalesOrder

getSalesTicket()

Configuration

HeaderDecorator2

HeaderDecorator2(Component) printTicket() printHeader()

FooterDecorator1

FooterDecorator1(Component) printTicket() printFooter()

FooterDecorator2

FooterDecorator2(Component) printTicket() printFooter()

SalesTicket

printTicket()

A SalesTicket Implementation

// Instances of this class are the sales tickets

// that may be decorated

public class SalesTicket extends Component {

public void printTicket() {

// Hard coded here, but simpler than

// adding a new Customer class now

System.out.println("Customer: Bob");

System.out.println("The sales ticket itself");

System.out.println("Total: $123.45");

}

}

Page 18: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

TicketDecorator

printTicket()

component

TicketDecorator

HeaderDecorator1

HeaderDecorator1(Component) printTicket() printHeader()

Component

TicketDecorator(Component) printTicket()

printTicket() main(String[])

SalesOrder

getSalesTicket()

Configuration

HeaderDecorator2

HeaderDecorator2(Component) printTicket() printHeader()

FooterDecorator1

FooterDecorator1(Component) printTicket() printFooter()

FooterDecorator2

FooterDecorator2(Component) printTicket() printFooter()

SalesTicket

printTicket()

public abstract class TicketDecorator extends Component {

private Component component;

public TicketDecorator(Component c) {

component = c;

}

public void printTicket() {

if(component != null)

component.printTicket();

}

}

Page 19: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

printTicket()

component

TicketDecorator

HeaderDecorator1

HeaderDecorator1(Component) printTicket() printHeader()

Component

TicketDecorator(Component) printTicket()

printTicket() main(String[])

SalesOrder

getSalesTicket()

Configuration

HeaderDecorator2

HeaderDecorator2(Component) printTicket() printHeader()

FooterDecorator1

FooterDecorator1(Component) printTicket() printFooter()

FooterDecorator2

FooterDecorator2(Component) printTicket() printFooter()

SalesTicket

printTicket()

A Header Decorator public class HeaderDecorator1 extends TicketDecorator {

public HeaderDecorator1(Component c) {

super(c);

}

public void printTicket() {

this.printHeader();

super.printTicket();

}

public void printHeader() {

System.out.println("@@ Header One @@");

}

}

Page 20: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Example – Sales Ticket Printing

printTicket()

component

TicketDecorator

HeaderDecorator1

HeaderDecorator1(Component) printTicket() printHeader()

Component

TicketDecorator(Component) printTicket()

printTicket() main(String[])

SalesOrder

getSalesTicket()

Configuration

HeaderDecorator2

HeaderDecorator2(Component) printTicket() printHeader()

FooterDecorator1

FooterDecorator1(Component) printTicket() printFooter()

FooterDecorator2

FooterDecorator2(Component) printTicket() printFooter()

SalesTicket

printTicket()

public class FooterDecorator2 extends TicketDecorator {

public FooterDecorator2(Component c) {

super(c);

}

public void printTicket() {

super.printTicket();

this.printFooter();

}

public void printFooter() {

System.out.println("## FOOTER Two ##");

}

}

Page 21: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

SalesOrder (Client)

printTicket()

component

TicketDecorator

HeaderDecorator1

HeaderDecorator1(Component) printTicket() printHeader()

Component

TicketDecorator(Component) printTicket()

printTicket() main(String[])

SalesOrder

getSalesTicket()

Configuration

HeaderDecorator2

HeaderDecorator2(Component) printTicket() printHeader()

FooterDecorator1

FooterDecorator1(Component) printTicket() printFooter()

FooterDecorator2

FooterDecorator2(Component) printTicket() printFooter()

SalesTicket

printTicket()

public class SalesOrder {

public static void main(String[] args) {

SalesOrder s = new SalesOrder();

s.printTicket();

}

public void printTicket() {

// Get an object decorated dynamically

Component myST = Configuration.getSalesTicket();

myST.printTicket();

}

// calcSalesTax ...

}

Page 22: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Example Configuration

printTicket()

component

TicketDecorator

HeaderDecorator1

HeaderDecorator1(Component) printTicket() printHeader()

Component

TicketDecorator(Component) printTicket()

printTicket() main(String[])

SalesOrder

getSalesTicket()

Configuration

HeaderDecorator2

HeaderDecorator2(Component) printTicket() printHeader()

FooterDecorator1

FooterDecorator1(Component) printTicket() printFooter()

FooterDecorator2

FooterDecorator2(Component) printTicket() printFooter()

SalesTicket

printTicket()

// This object will determine how to decorate the

// SalesTicket. This could become a Factory

public class Configuration {

public static Component getSalesTicket() {

// Return a decorated SalesTicket

return

new HeaderDecorator1(

new HeaderDecorator2(

new FooterDecorator1(

new FooterDecorator2(

new SalesTicket() ))));

}

}

Page 23: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Output with Current Configuration

¿  Output:

@@ Header One @@

>> Header Two <<

Customer: Bob

The sales ticket itself

Total: $123.45

%% FOOTER One %%

## FOOTER Two ##

Page 24: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Implementation Issues

¿  Keep Decorators lightweight ¿  Don't put data members in Component ¿  Use it for shaping the interface

¿  Omitting the abstract Decorator class ¿  If only one decoration is needed ¿  Subclasses may pay for what they don't need

Page 25: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Horizontal Vertical

Scrollbar

Vertical Scrollbar

Horizontal Scrollbar

VisualComponent

TextView

component

VisualDecorator

Scrollbar Border

PlainBorder 3DBorder FancyBorder

Return to TextView Example

¿  The TextView class knows nothing about Borders and Scrollbars

public class TextView {

public void draw() {

// Code to draw this Text object

}

public void resize () {

// Code to resize this Text object

}

}

Page 26: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

A New Class

¿  The new ImageView class knows nothing about Borders and Scrollbars

public class ImageView {

public void draw() {

// Code to draw this Image Object

}

public void resize () {

// Code to resize this Image Object

}

}

Page 27: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Horizontal Vertical

Scrollbar

Vertical Scrollbar

Horizontal Scrollbar

VisualComponent

TextView

component

VisualDecorator

Scrollbar Border

PlainBorder 3DBorder FancyBorder

Decorators Contain Components

¿  The decorators don’t need to know about components

public class FancyBorder extends Border {

public FancyBorder(VisualComponent c) {

super(c);

}

public void draw() {

// forward draw message

component.draw();

// Code to draw this FancyBorder object

}

}

Page 28: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

How to Use Decorators

public class Client {

public static void main(String[] args) {

TextView data = new TextView();

Component borderData = new FancyBorder(data);

Component scrolledData = new VertScrollbar(data);

Component borderAndScrolledData = new HorzScrollbar(borderData);

}

}

Page 29: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Decorator Pattern in Java

Page 30: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Decorator Pattern in Java

BufferedReader keyboard =

new BufferedReader(

new InputStreamReader(System.in));

public class JavaIO {

public static void main(String[] args) {

// Open an InputStream.

FileInputStream in = new FileInputStream("test.dat");

// Create a buffered InputStream.

BufferedInputStream bin = new BufferedInputStream(in);

// Create a buffered, data InputStream.

DataInputStream dbin = new DataInputStream(bin);

// Create a buffered, pushback, data InputStream.

PushbackInputStream pbdbin = new PushbackInputStream(dbin);

}

}

Page 31: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Java Streams

¿  With > 60 streams in Java, you can create a wide variety of input and output streams ¿  This provides flexibility (good) ¿  It also adds complexity (bad) ¿  Flexibility made possible with inheritance and classes that

accept many different classes that extend the parameter

¿  You can have an InputStream instance or any instance of a class that extends InputStream

public InputStreamReader(InputStream in)

Page 32: Decorator Pattern - cs356.yusun.iocs356.yusun.io/slides/L16-Decorator.pdf · Decorator Pattern. Decorator ! Intent ! Dynamically attach additional responsibilities to an object !

Consequences

Ë  Transparency – very good Ë  More flexibility than static inheritance

¿  Allows to mix and match responsibilities ¿  Allows to apply a property twice

Ë  Avoid feature-laden classes high-up in the hierarchy ¿  “Pay-as-you-go” approach ¿  Easy to define new types of decorations

Ò  A decorator and its component aren't identical Ò  Lots of little objects

¿  Easy to customize, but hard to learn and debug