Decorator and Factory
Kenneth M. Anderson
University of Colorado, Boulder
CSCI 4448/5448 — Lecture 18 — 10/22/2009
© University of Colorado, 2009
1
Lecture Goals
Cover Material from Chapters 3 and 4 of the Design Patterns Textbook
Decorator Pattern
Factory and Abstract Factory Pattern
2
Decorator Pattern
The Decorator Pattern provides a powerful mechanism for adding new
behaviors to an object at run-time
The mechanism is based on the notion of “wrapping” which is just a fancy
way of saying “delegation” but with the added twist that the delegator and
the delegate both implement the same interface
You start with object A that implements abstract type X
You then create object B that also implements abstract type X
You pass A into B’s constructor and then pass B to As client
The client thinks its talking to A but its actually talking to B
B’s methods augment As methods to provide new behavior
3
Why? Open-Closed Principle
The decorator pattern provides yet another way in which a class’s runtime
behavior can be extended without requiring modification to the class
This supports the goal of the open-closed principle:
Classes should be open for extension but closed to modification
Inheritance is one way to do this, but composition and delegation are
more flexible (and Decorator takes advantage of delegation)
Chapter 3’s “Starbuzz Coffee” example clearly demonstrates why inheritance
can get you into trouble and why delegation/composition provides greater
run-time flexibility
4
Starbuzz Coffee
Under pressure to update their “point of sale” system to keep up with their
expanding set of beverage products
Started with a Beverage abstract base class and four implementations:
HouseBlend, DarkRoast, Decaf, and Espresso
Each beverage can provide a description and compute its cost
But they also offer a range of condiments including: steamed milk, soy,
and mocha
These condiments alter a beverage’s description and cost
“Alter” is a key word here since it provides a hint that we might be
able to use the Decorator pattern
5
Initial Starbuzz System
getDescription()
cost()
description
Beverage
«Abstract»
cost()
HouseBlend
cost()
DarkRoast
cost()
Decaf
cost()
Espresso
With inheritance on your brain, you may add condiments to this design in
one of two ways
1. One subclass per combination of condiment (wont work in general but especially not in Boulder!)
2. Add condiment handling to the Beverage superclass
6
Approach One: One Subclass per Combination
getDescription()
cost()
description
Beverage
«Abstract»
cost()
HouseBlend
cost()
DarkRoast
cost()
Decaf
cost()
Espresso
cost()
HouseBlendWithSteamedMilkandMocha
cost()
HouseBlendWithSoyandMocha
cost()
EspressoWithSoyAndMocha
cost()
DecafWithWhipandSoy
This is incomplete, but you can see the problem…
(see page 81 for a more complete picture)
7
Approach Two: Let Beverage Handle Condiments
getDescription()
hasMilk()
setMilk()
hasSoy()
...
cost()
description
milk
soy
mocha
whip
Beverage
«Abstract»
cost()
HouseBlend
cost()
DarkRoast
cost()
Decaf
cost()
Espresso
Houston, we have a problem…
1. This assumes that all concrete Beverage classes need these condiments
2. Condiments may vary (old ones go, new ones are added, price changes,
etc.), shouldn’t they be encapsulated some how?
3. How do you handle “double soy” drinks with boolean variables?
8
Decorator Pattern: Definition and Structure
methodA()
methodB()
Component
methodA()
methodB()
...
att1
att2
ConcreteComponent
methodA()
methodB()
Decorator
methodA()
methodB()
...
ConcreteDecoratorA
methodA()
methodB()
...
newatt1
newatt2
ConcreteDecoratorB
component
Inheritance is used to make
sure that components and
decorators share the same
interface: namely the public
interface of Component
which is either an abstract
class or an interface
At run-time, concrete
decorators wrap
concrete components
and/or other concrete
decorators
The object to be
wrapped is typically
passed in via the
constructor
9
Client Perspective
Concrete
DecoratorB
Concrete
DecoratorA
Client
Concrete
Component
foo()
Concrete
Component
Client
foo() foo()
foo()
BEFORE
AFTER
In both situations,
Client thinks its talking to
a Component. It shouldn’t
know about the concrete
subclasses. Why?
10
StarBuzz Using Decorators (Incomplete)
11
Demonstration
Starbuzz Example
Use of Decorator Pattern in java.io package
InputStream == Component
FilterInputStream == Decorator
FileInputStream, StringBufferInputStream, etc. == ConcreteComponent
BufferedInputStream, LineNumberInputStream, etc. == ConcreteDecorator
12
Factory Pattern: The Problem With “New”
Each time we invoke the “new” command to create a new object, we violate
the “Code to an Interface” design principle
Example
Duck duck = new DecoyDuck()
Even though our variable’s type is set to an “interface”, the class that
contains this statement depends on “DecoyDuck”
In addition, if you have code that checks a few variables and instantiates a
particular type of class based on the state of those variables, then the
containing class depends on each referenced concrete class
if (hunting) {
return new DecoyDuck()
} else {
return new RubberDuck();
}
Obvious Problems:
needs to be recompiled each time a dep. changes;
add new classes, change this code;
remove existing classes, change this code
This means that this code violates the open-closed principle
and the “encapsulate what varies” design principle
13
PizzaStore Example
We have a pizza store program that wants to separate the process of creating
a pizza with the process of preparing/ordering a pizza
Initial Code: mixed the two processes
public class PizzaStore {
1
2
Pizza orderPizza(String type) {
3
4
Pizza pizza;
5
6
if (type.equals("cheese")) {
7
pizza = new CheesePizza();
8
} else if (type.equals("greek")) {
9
pizza = new GreekPizza();
10
} else if (type.equals("pepperoni")) {
11
pizza = new PepperoniPizza();
12
}
13
14
pizza.prepare();
15
pizza.bake();
16
pizza.cut();
17
pizza.box();
18
19
return pizza;
20
}
21
22
}
23
24
Creation
Preparation
Note: excellent example of “coding to an interface”
Creation code has all the
same problems as the
code on the previous slide
14
Encapsulate Creation Code
A simple way to encapsulate this code is to put it in a separate class
That new class depends on the concrete classes, but those dependencies
no longer impact the preparation code
public class PizzaStore {
1
2
private SimplePizzaFactory factory;
3
4
public PizzaStore(SimplePizzaFactory factory) {
5
this.factory = factory;
6
}
7
8
public Pizza orderPizza(String type) {
9
10
Pizza pizza = factory.createPizza(type);
11
12
pizza.prepare();
13
pizza.bake();
14
pizza.cut();
15
pizza.box();
16
17
return pizza;
18
}
19
20
}
21
22
public class SimplePizzaFactory {
1
2
public Pizza createPizza(String type) {
3
if (type.equals("cheese")) {
4
return new CheesePizza();
5
} else if (type.equals("greek")) {
6
return new GreekPizza();
7
} else if (type.equals("pepperoni")) {
8
return new PepperoniPizza();
9
}
10
}
11
12
}
13
14
15
Class Diagram of New Solution
orderPizza():
Pizza
PIzzaStore
createPizza(): Pizza
SimplePizzaFactory
prepare()
bake()
cut()
box()
Pizza
CheesePizza VeggiePizza PepperoniPizza
factory
Client
Products
Factory
While this is nice, its not as flexible as it can be: to increase flexibility we
need to look at two design patterns: Factory Method and Abstract Factory
16
Factory Method
To demonstrate the factory method pattern, the pizza store example evolves
to include the notion of different franchises
that exist in different parts of the country (California, New York, Chicago)
Each franchise will need its own factory to create pizzas that match the
proclivities of the locals
However, we want to retain the preparation process that has made
PizzaStore such a great success
The Factory Method Design Pattern allows you to do this by
placing abstract, “code to an interface” code in a superclass
placing object creation code in a subclass
PizzaStore becomes an abstract class with an abstract createPizza() method
We then create subclasses that override createPizza() for each region
17
New PizzaStore Class
public abstract class PizzaStore {
1
2
protected abstract createPizza(String type);
3
4
public Pizza orderPizza(String type) {
5
6
Pizza pizza = createPizza(type);
7
8
pizza.prepare();
9
pizza.bake();
10
pizza.cut();
11
pizza.box();
12
13
return pizza;
14
}
15
16
}
17
18
Factory Method
Beautiful Abstract Base Class!
This class is a (very simple) OO
framework. The framework provides
one service “prepare pizza”.
The framework invokes the
createPizza() factory method to create
a pizza that it can prepare using a well-
defined, consistent process.
A “client” of the framework will
subclass this class and provide an
implementation of the createPizza()
method.
Any dependencies on concrete
“product” classes are encapsulated in
the subclass.
18
New York Pizza Store
public class NYPizzaStore extends PizzaStore {
1
public Pizza createPizza(String type) {
2
if (type.equals("cheese")) {
3
return new NYCheesePizza();
4
} else if (type.equals("greek")) {
5
return new NYGreekPizza();
6
} else if (type.equals("pepperoni")) {
7
return new NYPepperoniPizza();
8
}
9
return null;
10
}
11
}
12
13
Nice and Simple. If you want a NY-Style Pizza, you create an instance of
this class and call orderPizza() passing in the type. The subclass makes
sure that the pizza is created using the correct style.
If you need a different style, create a new subclass.
19
Factory Method: Definition and Structure
The factory method design pattern defines an interface for creating an object,
but lets subclasses decide which class to instantiate. Factory Method lets a
class defer instantiation to subclasses
Product
ConcreteProduct
factoryMethod(): Product
operation()
Creator
factoryMethod(): ConcreteProduct
ConcreteCreator
Factory Method leads to the creation of parallel class hierarchies;
ConcreteCreators produce instances of ConcreteProducts that
are operated on by Creator’s via the Product interface
20
Dependency Inversion Principle
Factory Method is one way of following the dependency inversion principle
“Depend upon abstractions. Do not depend upon concrete classes.”
Normally “high-level” classes depend on “low-level” classes;
Instead, they BOTH should depend on an abstract interface
21
Dependency Inversion Principle: Pictorially
22
Level 1
Level 2
Client
Concrete
Service
Here we have a client class in an “upper”
level of our design depending on a
concrete class that is “lower” in the design
Dependency Inversion Principle: Pictorially
23
Level 1
Level 2
Client
Concrete
Service
Service
Interface
Instead, create an interface that lives in
the upper level that hides the concrete
classes in the lower level; “code to an
interface”
Dependency Inversion Principle
Factory Method is one way of following the dependency inversion principle
“Depend upon abstractions. Do not depend upon concrete classes.”
Normally “high-level” classes depend on “low-level” classes;
Instead, they BOTH should depend on an abstract interface
DependentPizzaStore depends on eight concrete Pizza subclasses
PizzaStore, however, depends on the Pizza interface
as do the Pizza subclasses
In this design, PizzaStore (the high-level class) no longer depends on the
Pizza subclasses (the low level classes); they both depend on the abstraction
“Pizza”. Nice.
24
Dependency Inversion Principle: How To?
To achieve the dependency inversion principle in your own designs, follow
these GUIDELINES
No variable should hold a reference to a concrete class
No class should derive from a concrete class
No method should override an implemented method of its base classes
These are “guidelines” because if you were to blindly follow these
instructions, you would never produce a system that could be compiled or
executed
Instead use them as instructions to help optimize your design
And remember, not only should low-level classes depend on abstractions, but
high-level classes should to… this is the very embodiment of “code to an
interface”
25
Demonstration
Lets look at some code
The FactoryMethod directory of this lecture’s example source code
contains an implementation of the pizza store using the factory method
design pattern
It even includes a file called “DependentPizzaStore.java” that shows
how the code would be implemented without using this pattern
DependentPizzaStore is dependent on 8 different concrete classes and 1
abstract interface (Pizza)
PizzaStore is dependent on just the Pizza abstract interface (nice!)
Each of its subclasses is only dependent on 4 concrete classes
furthermore, they shield the superclass from these dependencies
26
Moving On
The factory method approach to the pizza store is a big success allowing our
company to create multiple franchises across the country quickly and easily
But, bad news, we have learned that some of the franchises
while following our procedures (the abstract code in PizzaStore forces
them to)
are skimping on ingredients in order to lower costs and increase
margins
Our company’s success has always been dependent on the use of fresh,
quality ingredients
so “Something Must Be Done!” ®
27
Abstract Factory to the Rescue!
We will alter our design such that a factory is used to supply the ingredients
that are needed during the pizza creation process
Since different regions use different types of ingredients, we’ll create
region-specific subclasses of the ingredient factory to ensure that the right
ingredients are used
But, even with region-specific requirements, since we are supplying the
factories, we’ll make sure that ingredients that meet our quality standards
are used by all franchises
They’ll have to come up with some other way to lower costs.
28
First, We need a Factory Interface
public interface PizzaIngredientFactory {
1
2
public Dough createDough();
3
public Sauce createSauce();
4
public Cheese createCheese();
5
public Veggies[] createVeggies();
6
public Pepperoni createPepperoni();
7
public Clams createClam();
8
9
}
10
11
Note the introduction of more abstract classes: Dough, Sauce, Cheese,
etc.
29
Second, We implement a Region-Specific Factory
public class ChicagoPizzaIngredientFactory
1
implements PizzaIngredientFactory
2
{
3
4
public Dough createDough() {
5
return new ThickCrustDough();
6
}
7
8
public Sauce createSauce() {
9
return new PlumTomatoSauce();
10
}
11
12
public Cheese createCheese() {
13
return new MozzarellaCheese();
14
}
15
16
public Veggies[] createVeggies() {
17
Veggies veggies[] = { new BlackOlives(),
18
new Spinach(),
19
new Eggplant() };
20
return veggies;
21
}
22
23
public Pepperoni createPepperoni() {
24
return new SlicedPepperoni();
25
}
26
27
public Clams createClam() {
28
return new FrozenClams();
29
}
30
}
31
32
This factory ensures that
quality ingredients are used
during the pizza creation
process…
… while also taking into
account the tastes of people
who live in Chicago
But how (or where) is this
factory used?
30
Within Pizza Subclasses… (I)
public abstract class Pizza {
1
String name;
2
3
Dough dough;
4
Sauce sauce;
5
Veggies veggies[];
6
Cheese cheese;
7
Pepperoni pepperoni;
8
Clams clam;
9
10
abstract void prepare();
11
12
void bake() {
13
System.out.println("Bake for 25 minutes at 350");
14
}
15
16
void cut() {
17
System.out.println("Cutting the pizza into diagonal slices");
18
}
19
20
void box() {
21
System.out.println("Place pizza in official PizzaStore box");
22
}
23
24
void setName(String name) {
25
this.name = name;
26
}
27
28
String getName() {
29
return name;
30
}
31
32
public String toString() {
33
StringBuffer result = new StringBuffer();
34
result.append("---- " + name + " ----\n");
35
if (dough != null) {
36
result.append(dough);
37
result.append("\n");
38
}
39
if (sauce != null) {
40
result.append(sauce);
41
result.append("\n");
42
}
43
if (cheese != null) {
44
result.append(cheese);
45
result.append("\n");
46
}
47
if (veggies != null) {
48
for (int i = 0; i < veggies.length; i++) {
49
result.append(veggies[i]);
50
if (i < veggies.length-1) {
51
result.append(", ");
52
}
53
}
54
result.append("\n");
55
}
56
if (clam != null) {
57
result.append(clam);
58
result.append("\n");
59
}
60
First, alter the Pizza abstract base class to make the prepare method
abstract…
31
Within Pizza Subclasses… (II)
public class CheesePizza extends Pizza {
1
PizzaIngredientFactory ingredientFactory;
2
3
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
4
this.ingredientFactory = ingredientFactory;
5
}
6
7
void prepare() {
8
System.out.println("Preparing " + name);
9
dough = ingredientFactory.createDough();
10
sauce = ingredientFactory.createSauce();
11
cheese = ingredientFactory.createCheese();
12
}
13
}
14
15
Then, update Pizza subclasses to make use of the factory! Note: we no
longer need subclasses like NYCheesePizza and ChicagoCheesePizza
because the ingredient factory now handles regional differences
32
One last step…
public class ChicagoPizzaStore extends PizzaStore {
1
2
protected Pizza createPizza(String item) {
3
Pizza pizza = null;
4
PizzaIngredientFactory ingredientFactory =
5
new ChicagoPizzaIngredientFactory();
6
7
if (item.equals("cheese")) {
8
9
pizza = new CheesePizza(ingredientFactory);
10
pizza.setName("Chicago Style Cheese Pizza");
11
12
} else if (item.equals("veggie")) {
13
14
pizza = new VeggiePizza(ingredientFactory);
15
pizza.setName("Chicago Style Veggie Pizza");
16
17
} else if (item.equals("clam")) {
18
19
pizza = new ClamPizza(ingredientFactory);
20
pizza.setName("Chicago Style Clam Pizza");
21
22
} else if (item.equals("pepperoni")) {
23
24
pizza = new PepperoniPizza(ingredientFactory);
25
pizza.setName("Chicago Style Pepperoni Pizza");
26
27
}
28
return pizza;
29
}
30
}
31
32
We need to update our PizzaStore subclasses to create the appropriate
ingredient factory and pass it to each Pizza subclass in the createPizza
factory method.
33
Summary: What did we just do?
1. We created an ingredient factory interface to allow for the creation of a family
of ingredients for a particular pizza
2. This abstract factory gives us an interface for creating a family of products
2.1.The factory interface decouples the client code from the actual factory
implementations that produce context-specific sets of products
3. Our client code (PizzaStore) can then pick the factory appropriate to its
region, plug it in, and get the correct style of pizza (Factory Method) with the
correct set of ingredients (Abstract Factory)
34
35
Class Diagram of Abstract Factory Solution
createPizza()
orderPizza()
PizzaStore
bake()
cut()
box()
prepare()
Dough
Sauce
Cheese
...
Pizza
createDough(): Dough
createSauce(): Sauce
createCheese(): Cheese
...
PizzaIngredientFactory
prepare()
CheesePizza
createPizza()
ChicagoPizzaStore
createDough(): Dough
createSauce(): Sauce
createCheese(): Cheese
...
ChicagoPizzaIngredientFactory
Dough
ThickCrustDough
Note: Lots of
classes not shown
Demonstration
Lets take a look at the code
36
Abstract Factory: Definition and Structure
The abstract factory design pattern provides an interface for creating families
of related or dependent objects without specifying their concrete classes
Client
createProductA(): AbstractProductA
createProductB(): AbstractProductB
AbstractFactory
«Interface»
createProductA(): ProductA1
createProductB(): ProductB1
ConcreteFactoryA
createProductA(): ProductA2
createProductB(): ProductB2
ConcreteFactoryB
AbstractProductA
«Interface»
AbstractProductB
«Interface»
ProductA1 ProductA2
ProductB1 ProductB2
factory
37
Wrapping Up
Decorator
Way to implement open-closed principle that
makes use of inheritance to share an interface between a set of
components and a set of decorators
makes use of composition and delegation to dynamically wrap
decorators around components at run-time
All factories encapsulate object creation
Simple Factory is not a true pattern but is simple to understand/implement
Factory Method relies on inheritance: object creation occurs in subclasses
Abstract Factory relies on composition: object creation occurs in concrete factories
Both can be used to shield your applications from concrete classes
And both help apply the dependency inversion principle in your designs
38
Coming Up Next
Lectures 19 and 20: Framework Project Presentations
Lecture 21: Singleton, Command & Adaptor Patterns
Read Chapters 5 — 7 of the Design Patterns Textbook
Lecture 22: Template Methods, Iterator & Composite
Read Chapters 8 — 10 of the Design Patterns Textbook
39