Decorator Intent Dynamically attach additional responsibilities to - - PowerPoint PPT Presentation

decorator
SMART_READER_LITE
LIVE PREVIEW

Decorator Intent Dynamically attach additional responsibilities to - - PowerPoint PPT Presentation

Decorator Pattern CS356 Object-Oriented Design and Programming http://cs356.yusun.io November 7, 2014 Yu Sun, Ph.D. http://yusun.io yusun@csupomona.edu Decorator Intent Dynamically attach additional responsibilities to an object


slide-1
SLIDE 1

CS356 Object-Oriented Design and Programming http://cs356.yusun.io November 7, 2014 Yu Sun, Ph.D. http://yusun.io yusun@csupomona.edu

Decorator Pattern

slide-2
SLIDE 2

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

slide-3
SLIDE 3

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

slide-4
SLIDE 4

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

slide-5
SLIDE 5

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

slide-6
SLIDE 6

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

slide-7
SLIDE 7

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) before or after any forwarding

slide-8
SLIDE 8

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();

slide-9
SLIDE 9

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();

slide-10
SLIDE 10

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();

slide-11
SLIDE 11

ConcreteDecorator

u Adds responsibilities to the component

Component ConcreteComponent Operation() Decorator ConcreteDecoratorB ConcreteDecoratorA Operation() Operation() AddedBehavior() Operation() addedState component

Decorator::Operation(); AddBehavior(); Component->Operation();

slide-12
SLIDE 12

Decorator

¿ Applicability

¿ Dynamically and transparently attach responsibilities to

  • bjects

¿ Responsibilities that can be withdrawn ¿ Extension by subclassing is impractical ¿ May lead to too many subclasses

slide-13
SLIDE 13

Example – Sales Ticket Printing

slide-14
SLIDE 14

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

slide-15
SLIDE 15

Decorator Approach

¿ A layered approach

¿ Start chain with decorators ¿ End with original object

Decorator 1 Decorator 2 Concrete Component

slide-16
SLIDE 16

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()

slide-17
SLIDE 17

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"); } }

slide-18
SLIDE 18

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(); } }

slide-19
SLIDE 19

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 @@"); } }

slide-20
SLIDE 20

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 ##"); } }

slide-21
SLIDE 21

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 ... }

slide-22
SLIDE 22

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() )))); } }

slide-23
SLIDE 23

Output with Current Configuration

¿ Output:

@@ Header One @@ >> Header Two << Customer: Bob The sales ticket itself Total: $123.45 %% FOOTER One %% ## FOOTER Two ##

slide-24
SLIDE 24

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

slide-25
SLIDE 25

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 } }

slide-26
SLIDE 26

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 } }

slide-27
SLIDE 27

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 } }

slide-28
SLIDE 28

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); } }

slide-29
SLIDE 29

Decorator Pattern in Java

slide-30
SLIDE 30

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); } }

slide-31
SLIDE 31

Java Streams

¿ With > 60 streams in Java, you can create a wide variety

  • f 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)

slide-32
SLIDE 32

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