You are on page 1of 30

Decorator Pattern Part 2

Implementation
Recap
Coffee Shop Ordering System Example
First Design

i o n
lo s
exp
s s
Cl a
Coffee Shop Design 2

rclass
upe
es
t h
l i t y in ate
t i o na opri asses
c r l
d fun ot app e subc
Ad t is n of th
tion
tha some f i ca
for mod i
l e for
b
n f l exi
I
Recap
• We had two designs, where inheritance was used to inherit behaviors
• The first design issue: too many classes (class explosion)
• The second design issue: Subclasses inherit functionality not
appropriate for them
• Both share common thing:
• inflexible design, adding functionality to cope with new requirement,
require modifying the code
Design Principle: Classes should be open for extension, but closed for modification
Goal
• is to be able to add functionalities to an object (decorate the object)
dynamically at runtime
• extend the code without modifying it
• in the coffee shop ordering system
• customer start with main beverage and decorate it with condiment(s)
dynamically
• without the need to have all possible combinations between main beverages
& condiments at compile time
Decorator Pattern Definition

attaches additional responsibilities to an object dynamically. Decorators


provide a flexible alternative to subclassing for extending functionality
Decorator Class Diagram
The object that will be decorated, add behavior to it dynamically
1. Decorator extends the same
super type as the object it is
going to decorate
2. also has reference to it. It
wraps the main component
(HAS-A) relationship

A reference to object to be wrapped


Key Points
• Decorators have the same type as the object they are decorating
• Multiple decorators can be used to decorate an object
• Decorator can delegate work to be done by the objects it decorates
• it can add its own behavior before or after delegation
Apply Decorator on the Coffee Shop
Ordering System
abstract component

concrete components
decorator

concrete decorators
Apply Decorator on the Coffee Shop
Ordering System
abstract component

concrete components
decorator

concrete decorators
Inheritance & Composition
• The decorator (CondimentDecorator) has two relation with the
component (Beverage)
• It is extending the Beverage (inheritance)
• this is to make sure that decorator is of the same type as the
component it is decorating (type matching)
• inheriting the type, not the behavior
• Is is holding a reference to it (composition)
• to get the behavior
Inheritance vs. Composition
• Adding new behavior
• with inheritance we need to modify the code of superclass
• with composition we can implement new decorator with out modifying the
code of super class
Implementation
Beverage Class
• Abstract class

• description variable can be set in subclasses public abstract class Beverage {

• has two methods String description = "Unknown Beverage";


• cost() is abstract
public String getDescription() {
• will be implemented by classes return description;
• getDescription() to return description
}

public abstract double cost();


}
Implement the concrete
components
(Beverages)
Apply Decorator on the Coffee Shop
Ordering System

concrete components
Concrete Components
• Extends the superclass (Beverage)

• Set the description in the constructor public class Espresso extends Beverage {

• description variable inherited from the public Espresso() {


superclass (Beverage)
//description inherited from the superclass
• implement the cost() description = "Espresso";
}
• only put the cost of the corresponding
components
public double cost() {
return 1.99;//it is own cost
}
}
Concrete Components
• Same thing

• set proper description public class HouseBlend extends Beverage {


public HouseBlend() {
• return correct cost description = "House Blend Coffee";
• A new Beverage can be easily added in the }
same way
public double cost() {
return .89;
}
}
Concrete Components/same thing

public class Decaf extends Beverage { public class DarkRoast extends Beverage {
public Decaf() { public DarkRoast() {
description = "Decaf Coffee"; description = "Dark Roast Coffee";
} }

public double cost() { public double cost() {


return 1.05; return .99;
} }
} }
Decorator (CondimentDecorator)
• Decorator extends main component (Beverage)

• to inherit the type public abstract class CondimentDecorator extends Beverage


{
• make decorator the same type as the object its
decorating
public abstract String getDescription();
• Abstract class extending another abstract class

• Can implement the parent’s class methods but }


doesn’t have to if not necessary

• It makes the getDescription method abstract to force


concrete decorators to have their one
implementation (their own description)
Implementing the concrete
decorators condiments
Apply Decorator on the Coffee Shop
Ordering System

decorator

concrete decorators
Concrete Decorator (Type of Condiments)
public class Mocha extends CondimentDecorator {
• Extends the main decorator (condiment Beverage beverage;
decorator)

• which extends Beverage public Mocha(Beverage beverage) {


this.beverage = beverage;
• so now Mocha is Beverage (inherits the type) }
• Mocha IS-A
public String getDescription() {
• Mocha return beverage.getDescription() + ", Mocha";
• CondimentDecorator (Decorator)
}

• Beverage public double cost() {


return .20 + beverage.cost();
}
}
Concrete Decorator (Type of Condiments)
• Instantiate the condiment with the Beverage public class Mocha extends CondimentDecorator {
Beverage beverage;
• reference to the super component

• pass it to the constructor public Mocha(Beverage beverage) {


this.beverage = beverage;
• This is Composition }
• gives us the flexibility to mix condiment with
any Beverage public String getDescription() {
return beverage.getDescription() + ", Mocha";
• For example:
}
• Beverage b = new Espresso();

• New Mocha (b)


public double cost() {
return .20 + beverage.cost();
}
Concrete Decorator (Type of Condiments)
public class Mocha extends CondimentDecorator {
• The getDescription inherited from Beverage beverage;
the Beverage
public Mocha(Beverage beverage) {
• Get the description of the object
we are decorating (Espresso for this.beverage = beverage;
example) }

• Referring to the object we are public String getDescription() {


decorating to get its description return beverage.getDescription() + ", Mocha";
is called delegation }
• Append to it the description of
public double cost() {
the condiment
return .20 + beverage.cost();
}
Concrete Decorator (Type of Condiments)
public class Mocha extends CondimentDecorator {
• Implement the cost
Beverage beverage;
• Inherited from the Abstract Component
(Beverage)
public Mocha(Beverage beverage) {
• condiment knows its cost (0.2 in this this.beverage = beverage;
example) }
• add to that the cost of the object it is
decorating (again delegation) public String getDescription() {
return beverage.getDescription() + ", Mocha";
}

public double cost() {


return .20 + beverage.cost();
}
All other concrete decorators
(condiments) are implemented in
the same way
public class Milk extends CondimentDecorator {
Beverage beverage;
• The other concrete decorators Condiments:
public Milk(Beverage beverage) {
this.beverage = beverage;
• Do their own work and delegate to the super type to do its work }
• See getDescription & cost
public String getDescription() {
return beverage.getDescription() + ", Milk";
}

public double cost() {


public class Whip extends CondimentDecorator { return .10 + beverage.cost();
Beverage beverage; }
}public class Soy extends CondimentDecorator {
public Whip(Beverage beverage) {
this.beverage = beverage; Beverage beverage;
}
public Soy(Beverage beverage) {
public String getDescription() { this.beverage = beverage;
return beverage.getDescription() + ", Whip";
}
}

public double cost() { public String getDescription() {


return .10 + beverage.cost(); return beverage.getDescription() + ", Soy";
} }
}
public double cost() {
return .15 + beverage.cost();
}
}
Usage Example public class CoffeeApp {
public static void main(String args[]) {
Beverage beverage1 = new Espresso();
• Now assume that the System.out.println(beverage.getDescription()
customer ordered the + " $" + beverage.cost());
following:

• Espresso Beverage beverage2 = new DarkRoast();//make a DarkRoast object


• DarkRoast with double beverage2 = new Mocha(beverage2); //make a DarkRoast object
Mocha and Whip beverage2 = new Mocha(beverage2); //make a DarkRoast object
beverage2 = new Whip(beverage2); //make a DarkRoast object

System.out.println(beverage2.getDescription()
+ " $" + beverage2.cost());
}
}

You might also like