The Factory Method Pattern
Prof. Dr. Eric
Dubuis
Berner Fachhochschule,
Engineering and Information Technology @ Biel
Course UML and Design Patterns of module Software Engineering and Design,
Preliminary Remark: Instance Creation
You create an instance, for example, by using the new operator:
1 // In a class:
2 private List<Person> = new ArrayList<>(); 3 …
Drawback:
Your class depends on concrete class ArrayList. This is sometimes
undesirable. Goal:
Find a way to create instances without introducing a dependency as shown above.
The
orderPizza()
Method of a Simple Pizza ShopIn class PizzaStore (complete):
1 public Pizza orderPizza() { 2
3 Pizza pizza = new Pizza();
4 5 pizza.prepare(); 6 pizza.bake(); 7 pizza.cut(); 8 pizza.box(); 9 10 11 return pizza; 12} creation of an object usage of an object Fixed coupling with class Pizza. Pizza +prepare() +bake() +cut() +box() PizzaStore +orderPizza() : Pizza Client
Adding Different Pizza Flavors
1 public Pizza orderPizza(String type) { 2 Pizza pizza;
3 if ("cheese".equals(type)) { 4 pizza = new CheesePizza();
5 } else if ("greek".equals(type)) { 6 pizza = new GreekPizza();
7 } ... {
8 pizza = new Pizza();
9 } 10 11
12 // Using a pizza as before... (omitted)
13 14 // Then: 15 return pizza; Pizza +prepare() +bake() +cut() +box() PizzaStore +orderPizza() : Pizza Client CheesePizza +prepare() +bake() +cut() +box()
Pizzas Start Having Local Styles NYCheesePizza +prepare() +bake() +cut() +box() ChicagoCheesePizza +prepare() +bake() +cut() +box() NYGreekPizza +prepare() +bake() +cut() +box() ChicagoGreekPizza +prepare() +bake() +cut() +box() Risk:
The users of your software start modifying class PizzaStore! Pizza +prepare() +bake() +cut() +box() PizzaStore +orderPizza(): Pizza
Franchising The Pizza Store
You hand over your code (licensed, of course!) and allow partners to adapt it.
• Your New York partner adapts like this:
1 // In the orderPizza() method of class PizzaStore: 2 if (“cheese”.equals(type)) {
3 pizza = new NYStyleCheesePizza(); 4 } else ... 5 6 pizza.prepare(); 7 pizza.cut(); 8 pizza.bake(); 9 pizza.box(); 10 return pizza;
• Your Chicago partner adapts analogous!
Problem: Your partners start modifying your code!
Watch the order of
the methods! Cut() and bake() have changed!
A Framework for the Pizza Store Goals:
• You want to keep the pizza making (prepare, bake, cut, and box) procedures
under your control
• But you want to give your partners the freedom to have their own regional
style. Idea:
• introduce a (protected)
createPizza() method
in class PizzaStore
• make createPizza() abstract
Pizza PizzaStore {abstract} +orderPizza(): Pizza
#createPizza(): Pizza {abstract}
NYStyleCheesePizza NYStyleGreekPizza
ChicagoStyleCheesePizza ChicagoStyleGreekPizza
Let subclasses of class PizzaStore decide on the concrete type!
The New
PizzaStore
Class1 public abstract class PizzaStore { 2
3 public final Pizza orderPizza(String type) { 4 Pizza pizza = createPizza(type);
5 6 pizza.prepare(); 7 pizza.bake(); 8 pizza.cut(); 9 pizza.box(); 10 11 return pizza; 12 } 13
14 protected abstract Pizza createPizza(String type); 15}
Allowing the Subclass to Decide
?
Pizza PizzaStore {abstract}
+orderPizza(:String): Pizza #createPizza(:String):Pizza {abstract} NewYorkStylePizzaStore #createPizza(:String):Pizza ChicagoStylePizzaStore #createPizza(:String):Pizza
A Concrete
PizzaStore
Class1 public class NewYorkStylePizzaStore extends PizzaStore { 2
3 protected Pizza createPizza(String type) { 4 if (“cheese”.equals(type)) {
5. return new NYStyleCheesePizza(); 6 } else if (“greek”.equals(type)) {
7 return new NYStyleGreekPizza(); 8 } ...
9
10 } else {
11 return new NYStyleDefaultPizza();
12 }
13 } 14}
A Closer Look to an Order orderPizza(“cheese”) aPizza aPizza createPizza(“cheese”) bake() cut() box() prepare() «create» : NewYorkPizzaStore aPizza : NewYorkStyle CheesePizza
Creator Classes, Parallel Class Hierarchies
Pizza PizzaStore {abstract}
+orderPizza(...) : Pizza #createPizza(...) : Pizza {abstract} NYStyleCheesePizza NYStylePeperoniPizza ChicagoStyleCheesePizza ChigacoStylePeperoniPizza NYStylePizzaStore #createPizza(...) : Pizza ChicagoStylePizzaStore #createPizza(...) : Pizza «create» «create» Class Pizza can
also be abstract Pizza orderPizza() { Pizza p = createPizza(); p.prepare(); ... return p; } factory methods
The Factory Method Pattern «create» void anOperation() { Product p = factoryMethod(); p. ...; // operate on p } Note:
Factory method can be public, too. Note: Product can be concrete, too. Note: Creator depends on or uses Product Creator {abstract} +anOperation() #factoryMethod() : Product {abstract} ConcreteCreator #factoryMethod() : Product Product {abstract} ... ConcreteProduct ...
Benefits, Drawbacks:
• The factory method: creation through inheritance. Decouples the creation of
an object from its usage.
• Factory Method pattern makes a design more customizable.
• Factory methods are routinely specified by an architectural framework, and
then implemented by the user of the framework.
• Factory Method pattern is for creating objects. A superclass specifies standard
behavior (using pure virtual "placeholders" for creation steps), and then delegates the creation details to subclasses that are supplied by the client.
• Factory methods can be public, too.
• Creator (PizzaStore) can be concrete, too. If so then we speak of a default
implementation for the Creator.
• Product (Pizza) can be concrete, too. If so then we speak of a default product.