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 ¿ Provide a flexible alternative to subclassing (static) ¿ Decorating object is transparent to the core component ¿ Also Known As – Wrapper
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
Motivation ¿ An inheritance solution requires 15 subclasses to represent each type of view 1. TextView-Plain 9. TextView-Plain-Horizontal-Vertical 2. TextView-3D 10. TextView-3D-Horizontal 3. TextView-Fancy 11. TextView-3D-Vertical 4. TextView-Horizontal 12. TextView-3D-Horizontal-Vertical 5. TextView-Vertical 13. TextView-Fancy-Horizontal 6. TextView-Horizontal-Vertical 14. TextView-Fancy-Vertical 7. TextView-Plain-Horizontal 15. TextView-Fancy-Horizontal-Vertical 8. TextView-Plain-Vertical
Solution 1: Use Object Composition TextView border scrollbar Border Scrollbar PlainBorder 3DBorder FancyBorder Horizontal Vertical Horizontal Scrollbar Scrollbar Vertical Scrollbar ¿ Is it Open-Closed? ¿ Can you add new features without affecting TextView? ¿ e.g., what about adding sound to a TextView?
Decorator Pattern Solution VisualComponent TextView VisualDecorator component Border Scrollbar PlainBorder 3DBorder FancyBorder Horizontal Vertical Horizontal Scrollbar Scrollbar Vertical Scrollbar ¿ Change the Skin, not the Guts! ¿ TextView has no borders or scrollbars! ¿ Add borders and scrollbars on top of a TextView
Structure What is significance of this? Component component ConcreteComponent Decorator Operation() Operation() Component->Operation(); ConcreteDecoratorA ConcreteDecoratorB Operation() Operation() Decorator::Operation(); AddedBehavior() AddBehavior(); addedState The decorator forwards requests to the component and may perform additional actions (such as drawing a border) before or after any forwarding
Component u Defines the interface for objects that can have responsibilities added dynamically Component component ConcreteComponent Decorator Operation() Operation() Component->Operation(); ConcreteDecoratorA ConcreteDecoratorB Operation() Operation() Decorator::Operation(); AddedBehavior() AddBehavior(); addedState
ConcreteComponent u The "base" object to which additional responsibilities can be added Component component ConcreteComponent Decorator Operation() Operation() Component->Operation(); ConcreteDecoratorA ConcreteDecoratorB Operation() Operation() Decorator::Operation(); AddedBehavior() AddBehavior(); addedState
Decorator u Maintains a reference to a Component object u Defines an interface conformant to Component's interface Component component ConcreteComponent Decorator Operation() Operation() Component->Operation(); ConcreteDecoratorA ConcreteDecoratorB Operation() Operation() Decorator::Operation(); AddedBehavior() AddBehavior(); addedState
ConcreteDecorator u Adds responsibilities to the component Component component ConcreteComponent Decorator Operation() Operation() Component->Operation(); ConcreteDecoratorA ConcreteDecoratorB Operation() Operation() Decorator::Operation(); AddedBehavior() AddBehavior(); addedState
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
Example – Sales Ticket Printing
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
Decorator Approach ¿ A layered approach ¿ Start chain with decorators ¿ End with original object Decorator Decorator Concrete 1 2 Component
Example – Sales Ticket Printing Component SalesOrder printTicket() printTicket() main(String[]) SalesTicket TicketDecorator Configuration printTicket() TicketDecorator(Component) printTicket() getSalesTicket() component FooterDecorator1 HeaderDecorator1 FooterDecorator1(Component) HeaderDecorator1(Component) printTicket() printTicket() printFooter() printHeader() FooterDecorator2 HeaderDecorator2 FooterDecorator2(Component) HeaderDecorator2(Component) printTicket() printTicket() printFooter() printHeader()
A SalesTicket Implementation Component SalesOrder printTicket() printTicket() main(String[]) // Instances of this class are the sales tickets SalesTicket TicketDecorator // that may be decorated Configuration printTicket() TicketDecorator(Component) public class SalesTicket extends Component { printTicket() getSalesTicket() public void printTicket() { component // Hard coded here, but simpler than // adding a new Customer class now System.out.println("Customer: Bob"); FooterDecorator1 HeaderDecorator1 System.out.println("The sales ticket itself"); System.out.println("Total: $123.45"); FooterDecorator1(Component) HeaderDecorator1(Component) printTicket() printTicket() } printFooter() printHeader() } FooterDecorator2 HeaderDecorator2 FooterDecorator2(Component) HeaderDecorator2(Component) printTicket() printTicket() printFooter() printHeader()
TicketDecorator Component SalesOrder printTicket() printTicket() main(String[]) SalesTicket TicketDecorator Configuration printTicket() TicketDecorator(Component) printTicket() getSalesTicket() component public abstract class TicketDecorator extends Component { private Component component; FooterDecorator1 HeaderDecorator1 public TicketDecorator(Component c) { FooterDecorator1(Component) HeaderDecorator1(Component) printTicket() component = c; printTicket() printFooter() printHeader() } FooterDecorator2 HeaderDecorator2 public void printTicket() { if(component != null) FooterDecorator2(Component) HeaderDecorator2(Component) component.printTicket(); printTicket() printTicket() printFooter() } printHeader() }
A Header Decorator Component public class HeaderDecorator1 extends TicketDecorator { SalesOrder printTicket() public HeaderDecorator1(Component c) { printTicket() main(String[]) super(c); } SalesTicket TicketDecorator public void printTicket() { Configuration printTicket() TicketDecorator(Component) this.printHeader(); printTicket() super.printTicket(); getSalesTicket() } component public void printHeader() { System.out.println("@@ Header One @@"); FooterDecorator1 HeaderDecorator1 } } FooterDecorator1(Component) HeaderDecorator1(Component) printTicket() printTicket() printFooter() printHeader() FooterDecorator2 HeaderDecorator2 FooterDecorator2(Component) HeaderDecorator2(Component) printTicket() printTicket() printFooter() printHeader()
Example – Sales Ticket Printing Component public class FooterDecorator2 extends TicketDecorator { SalesOrder printTicket() printTicket() public FooterDecorator2(Component c) { main(String[]) super(c); } SalesTicket TicketDecorator public void printTicket() { Configuration printTicket() TicketDecorator(Component) super.printTicket(); printTicket() getSalesTicket() this.printFooter(); component } public void printFooter() { System.out.println("## FOOTER Two ##"); FooterDecorator1 HeaderDecorator1 } FooterDecorator1(Component) HeaderDecorator1(Component) } printTicket() printTicket() printFooter() printHeader() FooterDecorator2 HeaderDecorator2 FooterDecorator2(Component) HeaderDecorator2(Component) printTicket() printTicket() printFooter() printHeader()
SalesOrder (Client) Component SalesOrder printTicket() printTicket() public class SalesOrder { main(String[]) public static void main(String[] args) { SalesOrder s = new SalesOrder(); SalesTicket TicketDecorator s.printTicket(); Configuration printTicket() TicketDecorator(Component) } printTicket() getSalesTicket() public void printTicket() { component // Get an object decorated dynamically Component myST = Configuration.getSalesTicket(); myST.printTicket(); FooterDecorator1 HeaderDecorator1 } FooterDecorator1(Component) HeaderDecorator1(Component) printTicket() printTicket() // calcSalesTax ... printFooter() printHeader() } FooterDecorator2 HeaderDecorator2 FooterDecorator2(Component) HeaderDecorator2(Component) printTicket() printTicket() printFooter() printHeader()
Recommend
More recommend