180 likes | 312 Views
The Decorator Pattern. Example: The “Ice Cream Store”. Ice Cream. #description. What kind of a class is Ice Cream ? What kind of a method is cost ? Why?. + getDescription () +cost(). Dish. Cone. WaffleCone. cost(). cost(). cost().
E N D
SE-2811 Dr. Mark L. Hornick
The Decorator Pattern SE-2811 Dr. Mark L. Hornick
Example: The “Ice Cream Store” Ice Cream #description What kind of a class is Ice Cream? What kind of a method is cost? Why? +getDescription() +cost() Dish Cone WaffleCone cost() cost() cost() Subclasses define their own implementation and return the cost of the IceCream.
Decorator Pattern Intent You want to attach additional functionality to an (existing) class dynamically… • …without having to resort to sub-classing the existing class • It’s also possible that the existing class is declared final, in which case you can’t subclass it • …or making any modifications to the existing class • Also, you may not have access to the existing class’s source
The “Ice Cream Store” (contd.) public abstract class IceCream { String description; public String getDescription() { return description; } public abstract double cost(); } Ice Cream description getDescription() cost() public class Cone extends IceCream { public Cone() { description = “Cone”; } public double cost() { return 1.24; } } Dish Cone cost() cost()
Extending functionality • Now in addition to your ice cream, you can also ask for additional toppings (Fudge, M&M’s, peanuts, …) • Charges are extra for each additional topping. • How should we design the system?
Alternative 1: Create a new class for each combination. Any problems with this approach?
Alternative 1: Create a new class for each combination. • Results in class explosion! • Maintenance nightmare! • What happens when a new topping is added? • What happens when the cost of a topping (e.g. Fudge) changes?
Alternative 2: Using instance variables to keep track of the toppings? IceCream -description: String -hasFudge: boolean -hasMnM: boolean ------- +getDescription() +cost() +hasFudge() +hasCaramel() ------
Alternative 2 (contd.) public class Icecream { ……………… public double cost() { double toppingCost = 0; if (hasFudge()) { toppingCost += oreoCost; } if (hasCaramel()) { toppingCost += caramelCost; } …. return toppingCost; } }
Alternative 2 (contd.) public class Cone{ ……………… public double cost() { return 1.24 + super.cost(); } } What is this method calculating and returning?
So what’s the problem? • We want to allow existing classes to be easily adapted to incorporate new behavior without modifying existing code. • We want a design that is flexible enough to take on new functionality to meet changing requirements. The Decorator Pattern comes to the rescue!!
SE-2811 Dr. Mark L. Hornick
SE-2811 Dr. Mark L. Hornick
Using the Decorator Pattern • Take the IceCream object • Let’s create the Cone object. • IceCream cone= new Cone(); • Decorate it with the Fudge topping. • Create a Fudge object and wrap it around Cone. • cone = new Fudge(); • Decorate it with the Nuts topping. • Create a Nuts object and wrap it around Cone. • cone = new Nuts(cone); • Call the cost method and rely on delegation to add the cost of all toppings. • Call cost on the outmost decorator. • System.out.println(cone.cost());
Summary • Decorators have the same super-type as the objects they decorate. • One or more decorators can be used to wrap an object. • Given that the decorator has the same super-type as the object it decorates, we can pass around a decorated object in place of the original (wrapped) object. • The decorator adds its own behavior either before and/or after delegating to the object it decorates to do the rest of the job. • Objects can be decorated at any time, so we can decorate objects at runtime with as many decorators as we like.