The Decorator Pattern CSCI 3132 Summer 2011 1 Starbuzz Coffee • Want to offer a variety of combina5ons of coffee and condiments • Cost of a cup depends on the combina5on that was ordered 2 First Design • Make a beverage class and a subclass for each legal combina5on <<abstract>> Beverage String description getDescription() <<abstract>> cost() … Espresso Decaf HouseBlend DarkRoast cost() cost() cost() cost() DecafWith EspressoWit HouseBlendWith SteamedMilk DarkRoastWith SteamedMilkSteamedMil Espresso cost () Decaf HouseBlendSteamedMilk cost () cost() DarkRoast Mocha cost() Mocha Mocha Mocha cost() Espresso cost() cost() Decaf HouseBlend cost() DarkRoast WithWhip WithWhip WithWhip WithWhip cost() cost() 3 cost() 3 Problems with Inheritance • Too many subclasses may be needed. • Need to define the implementa5on of each subclass. • All classes inherit a sta5c behaviour which may have to be overridden. • The inherited behaviour cannot be changed at run5me, i.e. behaviour is sta5c at run5me. • Thus, the design is not flexible or maintainable. 4 Second Design • Make the superclass contain booleans to specify which condiments are included and subclasses for each type of coffee • How do we compute cost? • What does it take to add a new condiment? 5 <<abstract>> HouseBlend cost() Beverage String description milk soy mocha whip getDescription() <<abstract>>cost () hasMilk() setMilk() hasSoy() setSoy() hasMocha() setMocha() hasWhip() setWhip() … DarkRoast cost() Decaf cost() Espresso cost() 6 Design Principle • Classes should be open for extension, but closed for modifica5on – “extension” is NOT subclassing – It means the addi5on of new behavior (without modifying the code!) 7 Decorator PaSern • Start with an instance of the “basic”classes and then decorate it with new capabili5es Whip Mocha Dark Roast cost() 0.99+0.20+0.10 cost() 0.99+0.20 cost() 0.99 8 Key Points • Decorators have the same supertypes as the objects they decorate – This lets us pass around the decorated object instead of the original (unwrapped) object • Decorator add behavior by delega5ng to the object it decorates and then adding its own behavior • Can add decora5ons at any 5me 9 Class Diagram for the Decorator PaSern 10 Starbuzz Coffee Example 11 public abstract class Beverage{! String description = “Unknown”;! public String getDescription() {! return description;! }! public abstract double cost();! }! public abstract class CondimentDecorator extends Beverage {! public abstract String getDescription();! public abstract double cost();! }! public class Espresso extends Beverage {! public Espresso() {! description = “Espresso”; ! }! public double cost() {! return 1.99;! }! }! 12 public class Mocha extends CondimentDecorator {! Beverage bev;! public Mocha(Beverage bev) {! this.bev = bev;! }! public String getDescription {! return bev.getDescription() + “, Mocha”;! }! public double cost() {! return .20 + bev.cost();! }! }! 13 public class StarBuzz {! public static void main(String args[]) {! Beverage bev = new Espresso); ! bev = new Mocha(bev);! bev = new Mocha(bev);! bev = new Whip(bev);! bev = new Soy(bev);! System.out.println(bev.getDescription() + “ $”+! bev.getCost()); ! }! 14 Decorators in Java • File I/O Example Concrete decorators Component BufferedInputStream LineNumberInputStream FileInputStream 15 Design Principle for the Decorator PaSern • Classes should be open for extension but closed for modifica5on. • Systems can be extended without changing exis5ng code. • Tradeoffs: – Takes 5me and effort. – Introduces new levels of abstrac5on which makes designs more complicated and code hard to understand. • Due to the tradeoffs the decorator paSern should not be used throughout the design but in areas that are most likely to change. • Experience and looking at other examples helps one determine which areas are likely to change. 16 Decorator PaSern • Provides a flexible alterna5ve to using inheritance to extend func5onality. • This achieved by introducing decorators that “decorate” objects. • Decorators have the same type as the objects they decorate. • An object can have one or more decorators. 17 The Decorator PaSern • The decorated object and the original object have the same type. Thus, the decorated object can be passed instead of the original object. • The decorator delegates part of its behaviour to the object it decorates. • It adds its own behaviour before or aZer the delega5on. • Objects can be delegated dynamically at run5me. • The objects are “wrapped” with decorators. 18 Decorator PaSern Summary • Main design principle: Classes should be open for extension but closed for modifica5on. • Achieves flexibility-­‐ behaviour can be extended without changing exis5ng code. • Composi5on and delega5on is used to add new behaviours at run5me. • A set of decorator classes are used to wrap concrete components. • Overuse of decorators can be complex as they can introduce a number of small objects into a design. 19