Professional Documents
Culture Documents
6-Factory Ex
6-Factory Ex
1
The Constitution of Software Architects
• Encapsulate what varies
• Program through an interface not to an
implementation
• Favor Composition over Inheritance
• Classes should be open for extension but closed for
modification
• ?????????
• ?????????
• ?????????
• ?????????
• ?????????
2
"new" = "concrete"
3
"new" = "concrete"
• Design Principle: "Program through an interface
not to an implementation"
• However, every time you do a "new" you need to
deal with a "concrete" class, not an abstraction.
With a whole set of related
Duck duck = new MallardDuck(); concrete classes:
Duck duck;
if (picnic)
We want to use But we have to
duck = new MallardDuck();
interfaces to keep create an instance
else if (hunting)
code flexible of a concrete class
duck = new DecoyDuck();
else if (inBathTub)
duck = new RubberDuck();
}
5
Identifying aspects that vary
• Order pizza in a pizza store in cutting edge Objectville!
6
Identifying aspects that Vary
• But you need more than one type of pizza:
public class PizzaStore {
Pizza orderPizza(String type) { Pass in the type of Pizza to
Pizza pizza; orderPizza()
8
Modified orderPizza()
public class PizzaStore {
Pizza orderPizza(String type) {
Pizza pizza;
Pizza
if (type.equals("cheese") {
+ prepare() pizza = new CheesePizza();
+ bake() This is what varies } else if (type.equals("greek")) {
+ cut() This code is not pizza = new GreekPizza();
+ box() closed for } else if (type.equals("pepperoni") {
modification!
pizza = new PepperoniPizza();
} else if (type.equals("clam") {
pizza = new ClamPizza();
CheesePizza PepperoniPizza
} else if (type.equals("veggie") {
pizza = new VeggiePizza();
ClamPizza VeggiePizza }
pizza.prepare();
pizza.bake(); This is what we expect
pizza.cut(); will stay the same
pizza.box();
return pizza;
} 9
}
Encapsulating Object Creation
• Move the object creation out of the orderPizza() method.
• How?
– Move the creation code into a special purpose object that is
concerned with only creating pizzas
public class PizzaStore {
Pizza orderPizza(String type) {
Pizza pizza; if (type.equals("cheese") {
pull it out pizza = new CheesePizza();
} else if (type.equals("pepperoni") {
pizza.prepare(); pizza = new PepperoniPizza();
pizza.bake(); } else if (type.equals("clam") {
pizza.cut(); pizza = new ClamPizza();
What’s going } else if (type.equals("veggie") {
pizza.box(); to go here?
return pizza; pizza = new VeggiePizza();
} }
}
We place this code into a separate
object SimplePizzaFactory
10
Building a Simple Pizza Factory
Factories handle the details of the object creation.
public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("cheese") { Here’s code we plucked out
pizza = new CheesePizza(); of the orderPizza() method.
} else if (type.equals("pepperoni") {
pizza = new PepperoniPizza();
} else if (type.equals("clam") { Code is still parameterized
pizza = new ClamPizza(); by the type of pizza.
} else if (type.equals("veggie") {
pizza = new VeggiePizza();
}
return pizza;
}
}
12
Why is this better?
• SimplePizzaFactory may have many more clients
than orderPizza() method
– PizzaShopMenu, HomeDelivery etc.
• Client code does not have any concrete classes
anymore!!
13
Simple Factory Pattern
Abstract class with some
The factory where we create pizzas.
helpful implementations
This should be the only part of our application
that can be overridden.
that refers to concrete Pizza classes.
Pizza
PizzaStore SimplePizzaFactory create Product of
+ prepare() the factory
- factory : PizzaFactory + bake()
+ createPizza() : Pizza (abstract)
+ cut()
+ orderPizza() : Pizza
+ box()
create method sometimes
Client of the factory. declared static.
Goes through factory to
get instances of Pizza
CheesePizza PepperoniPizza
VeggiePizza ClamPizza
Concrete products.
Each product needs to implement the Pizza interface.
14
Onwards with the Pizza Franchise
• Franchises in different cities
– Must ensure quality of pizza
– Must account for regional differences (NY, Chicago..)
• Want franchise store to leverage your PizzaStore
code --> pizzas are prepared the same way
• New York needs a factory that makes New York
style pizza
• Chicago needs a factory that makes Chicago style
pizza
• One approach --> Simple Factory
15
Applying SimpleFactory
PizzaStore Pizza
PizzaFactory name : String
factory : PizzaFactory
dough : String
orderPizza() : Pizza createPizza(): Pizza sauce : String
toppings : ArrayList
prepare()
bake()
ChicagoStylePizzaFactory cut()
box()
createPizza() : Pizza
NYStylePizzaFactory
NYStyleCheesePizza ChicagoStyleCheesePizza
createPizza() : Pizza NYStylePepperoniPizza ChicagoStylePepperoniPizza
NYStyleClamPizza ChicagoStyleClamPizza
NYStyleVeggiePizza ChicagoStyleVeggiePizza
create
create
16
Applying Simple Factory Pattern
Here we create a factory for
making NY style pizza
Then we create a
NYStylePizzaFactory nyFactory =
PizzaStore and pass it a
new NYStylePizzaFactory();
reference to the NY factory
PizzaStore nyStore = new PizzaStore(nyFactory);
nystore.orderPizza("Veggie");
17
Issues
• Franchises using your factory to create pizza, but
using homegrown procedures for baking, cut the
pizza, uses third-party boxes, etc.
• Yet, each franchise “needs room for adding own
improvements”
– You don’t want to know what they put on their pizza -
detail that should be “exposed” only to the individual
stores.
Yet you want to have some control (quality control!)
19
A Framework
• Need a mechanism to “localize” all pizza making
activities to the PizzaStore class and yet give
franchises freedom to have their own regional style!
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Now createPizza is back
Pizza pizza = createPizza(type); to being a call to a method
in the PizzaStore rather
pizza.prepare(); than a Factory object!
pizza.bake();
pizza.cut();
pizza.box(); Our factory method is now
return pizza; abstract in PizzaStore.
}
NYPizzaStore ChicagoPizzaStore
+ createPizza() + createPizza()
createPizza() returns a
Each subclass overrides the Pizza and the subclass is
createPizza() method, fully responsible for which
while all subclasses make use concrete Pizza it instantiates
of the orderPizza() method
defined in the PizzaStore.
21
Allowing the subclasses to decide…
PizzaStore
Pizza
+ createPizza() name : String
+ orderPizza() dough : String
sauce : String
toppings : ArrayList
prepare()
bake()
NYPizzaStore ChicagoPizzaStore cut()
box()
+ createPizza() + createPizza()
NYStyleCheesePizza ChicagoStyleCheesePizza
NYStylePepperoniPizza ChicagoStylePepperoniPizza
NYStyleClamPizza ChicagoStyleClamPizza
NYStyleVeggiePizza ChicagoStyleVeggiePizza
create
create
22
Let's make a PizzaStore
extends PizzaStore, so it inherits
the orderPizza() method
A factory method returns a Product A factory method isolates the client from
that is typically used within methods knowing what kind of concrete Product
defined in the superclass. is actually created.
24
Factory Method Pattern
• All factory patterns encapsulate “object creation”
• Factory method pattern encapsulates object
creation by letting subclasses decide what objects to
create.
• Who are the players in this pattern?
25
Factory Method Pattern Defined
The Factory Method Pattern defines an interface for creating an object
but lets the subclasses decide which class instantiate.
Factory method lets a class defer instantiation to subclasses.
Contains the implementations for all the The abstract factoryMethod() is what
methods to manipulate the products, all Creator subclasses must implement.
except for the factory method!
Creator
Product
... defines the
+ factoryMethod()
product = factoryMethod(); + anOperation()
interface of
... objects the
factory method
creates
implements the ConcreteCreator <<create>> ConcreteProduct
factoryMethod() that
actually produces the + factoryMethod()
products.
NYStyleCheesePizza CStyleCheesePizza
NYPizzaStore
NYStylePepperoniPizza CStylePepperoniPizza
NYStyleClamPizza CStyleClamPizza + createPizza()
NYStyleVeggiePizza CStyleVeggiePizza
ChicagoPizzaStore
+ createPizza()
NYPizzaStore encapsulate all the
knowledge about how to make NY ChicagoPizzaStore encapsulate all the
Style Pizzas
knowledge about how to make Chicago
Style Pizzas
pizza = chicagoStore.orderPizza("cheese");
System.out.println("Joel ordered a " +
pizza.getName() + "\n");
// ...
}
}
31
What we have learned from Factory Method
• First of all let’s take a look on what we tried to avoid
public class DependentPizzaStore {
public Pizza createPizza(String style, String type){
Pizza pizza = null;
if (style.equals("NY")) {
if (type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
} else if (type.equals("veggie")) {
pizza = new NYStyleVeggiePizza();
...
} else if (style.equals("Chicago")) {
if (type.equals("cheese")) {
pizza = new ChicagoStyleCheesePizza();
} else if (type.equals("veggie")) {
pizza = new ChicagoStyleVeggiePizza();
...
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
} 32
33
Design Principle
34
DIP explained
Depend upon abstractions.
Do not depend upon
concrete classes.
35
Guidlines to follow the DIP
• No variable should hold a reference to a concrete
class
– If you use new you are holding a reference to a concrete
class. Use a factory to get around that
• No class should derive from a concrete class
– If you derive from a concrete class, you’re depending on a
concrete class. Derive from an abstraction like an interface
or an abstract class.
• No method should override an implemented method
of any of its base classes
– If you override an implemented method, then your base
class wasn’t really an abstraction to start with.
Those methods implemented in the base class are meant
to be shared by all your subclasses.
36
Meanwhile, back at the PizzaStore….
• Things are going good, but you have learned that
a few franchises are substituting inferior ingredients
• How are you going to ensure each factory is using
quality ingredients?
– You are going to build a factory that produces them
and ships them to your franchises!
• One problem:
– Franchises are located in different regions so what is
red sauce in NY is not red sauce in Chicago!
• So: same product families (dough, cheese, sauce
etc.) but different implementations based on region.
37
Building the Ingredient Factory
• Ingredient factory: creates each ingredient in the
ingredient family (but does not handle regional
differences yet!)
38
What to do next?
• Build a factory for each region.
To do this, create a subclass of
PizzaIngredientFactory that implements each
create method.
• Implement a set of ingredient classes to be used
with the factory, like RedPeppers,
ThickCrustDough, etc. These classes can be
shared among regions where appropriate.
• Then we still need to hook all this up by working our
new ingredient factories into the old PizzaStore
code.
39
Abstract factory pattern
40
Build a factory for each region
<<Interface>>
PizzaIngredientFactory
createDough()
createSauce()
createVeggies()
createCheese()
createPepperoni()
createClams()
NYPizzaIngredientFactory ChicagoPizzaIngredientFactory
createDough() createDough()
createSauce() createSauce()
createVeggies() createVeggies()
createCheese() createCheese()
createPepperoni() createPepperoni()
createClams() createClams()
41
Build a factory for New york
…
<<Interface>> <<Interface>> <<Interface>>
PizzaIngredientFactory Dough Sauce
createDough()
createSauce()
createVeggies() ThinCrustDough
createCheese() MarinaraSauce
createPepperoni() ThickCrustDough PlumTomatoSauce
createClams()
<<Interface>>
NYPizzaIngredientFactory
<<Interface>> Veggies
Cheese
createDough()
createSauce()
createVeggies()
createCheese() Garlic BlackOlives
createPepperoni() Onion
createClams() ReggianoCheese Spinach
Mushroom
MozzarellaCheese Eggplant
RedPepper
42
(1) The New York Ingredient Factory
public class NYPizzaIngredientFactory
implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough();
The NY ingredient factory implements
}
the interface for all ingredient factories
public Sauce createSauce() {
return new MarinaraSauce();
}
public Cheese createCheese() { For each ingredient in the
return new ReggianoCheese(); ingredient family, we
} create the NY version.
public Veggies[] createVeggies() {
Veggies veggies[] =
{ new Garlic(), new Onion(),
new Mushroom(), new RedPepper() };
return veggies;
}
// other ingredients
}
43
Implement a set of ingredient classes
<<Interface>> <<Interface>> <<Interface>>
Dough Sauce Pepperoni
<<Interface>>
Veggies
<<Interface>> <<Interface>>
Cheese Clams
Garlic BlackOlives
Onion Spinach
FreshClams Mushroom
ReggianoCheese Eggplant
MozzarellaCheese FrozenClams RedPepper
44
Reworking the Pizzas
Pizza <<Interface>> <<Interface>> <<Interface>>
Dough Sauce Pepperoni
name : String
dough : Dough
sauce : Sauce
veggies : Veggies[]
cheese : Cheese ThinCrustDough MarinaraSauce SlicedPepperoni
pepperoni : Pepperoni ThickCrustDough PlumTomatoSauce
clam : Clams
prepare() <<Interface>>
bake() Veggies
cut()
box() <<Interface>> <<Interface>>
Cheese Clams
Garlic BlackOlives
Onion Spinach
FreshClams Mushroom
ReggianoCheese Eggplant
MozzarellaCheese FrozenClams RedPepper
45
Reworking the Pizzas
public abstract class Pizza {
String name;
Dough dough; Each pizza holds a set of ingredients
Sauce sauce; that are used in its prep.
Veggies veggies[];
Cheese cheese;
Pepperoni pepperoni; The prepare() method is abstract.
Clams clam; This is where we are going to collect the
ingredients needed for the pizza which
abstract void prepare();
will come from the ingredient factory.
void bake() {
System.out.println("Bake for 25 minutes at 350");
}
void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}
void box() {
System.out.println("Place pizza in official PizzaStore box");
}
void setName(String name) { this.name = name; }
String getName() { return name; }
}
46
Hook pizza working ingredient factories
Pizza
name : String
dough : Dough
saugh : Sauce
veggies : Veggies[]
cheese : Cheese
pepperoni : Pepperoni
clam : Clams
prepare()
bake()
cut()
box()
ClamPizza
CheesePizza factory : PizzaIngredientFactory
factory : PizzaIngredientFactory prepare() PepperoniPizza
factory : PizzaIngredientFactory
prepare() VeggiePizza
factory : PizzaIngredientFactory prepare()
prepare()
47
Reworking the Pizzas (cont)
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
48
Code Up Close!
The createSauce() method returns
We are setting the pizza the sauce that is used in its region.
instance variable to refer to the If this is NY ingredient factory, then
specific sauce used in this pizza we get marinara sauce.
sauce = ingredientFactory.createSauce();
49
Revisiting the Pizza Store
The NY Store is composed
public class NYPizzaStore extends PizzaStore { with a NY pizza ingredient
factory. This will be used to
protected Pizza createPizza(String item) { produce the ingredients for
Pizza pizza = null; all NY style pizzas.
PizzaIngredientFactory ingredientFactory =
new NYPizzaIngredientFactory();
if (item.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory); We now pass each
pizza.setName("New York Style Cheese Pizza"); pizza the factory that
} else if (item.equals("veggie")) { should be used to
pizza = new VeggiePizza(ingredientFactory); produce its ingredients
pizza.setName("New York Style Veggie Pizza");
} // same for all other pizza types.
return pizza;
}
} For each type of pizza we instantiate a
new Pizza and give it the factory that
it needs to get its ingredients.
50
What have we done? Defines the
interface
52
Structure This is the product family.
Each concrete factory can
produce an entire set of objects.
AbstractFactory
defines the interface
that all concrete
factories must
implement,
53
54
Summary
• All factories encapsulate object creation
• Simple factory, while not a bona fide design pattern,
is a simple way to decouple your clients from
concrete classes.
• Factory method relies on inheritance: object creation
is delegated to subclasses which implement the
factory method to create objects
• Abstract Factory relies on object composition: object
creation is implemented in methods exposed in the
factory interface.
55
Summary
• All factory methods promote loose coupling by reducing
the dependency of your application on concrete classes.
• The intent of Factory method is to allow a class to defer
instantiation to its subclasses.
• The intent of the Abstract Factory is to create families of
related objects without having to depend on their
concrete classes.
• The Dependency Inversion principle guides us to avoid
dependencies on concrete types and to strive for
abstractions.
• Factories are a powerful technique for coding to
abstractions, not concrete classes.
56
Đặt vấn đề
• Giả sử bạn phải viết phần mềm cho trung tâm giống
cây trồng phát giống các bác nông dân dựa vào vị
trí thửa đất của họ. Biết phần mềm đang áp dụng
cho 2 khu vực là Tây nguyên và đồng bằng Sông
Cửu Long. Với tiêu chí
• Tây nguyên: cho phép chọn bất kỳ cây công nghiệp
nào trong danh sách: Cà phê, tiêu, cao su.
• Đồng bằng sông Cửu Long: cho phép chọn tuần tự
các cây ăn trái trong danh sách: Cam, Xoài, Nhãn,
Sầu riêng.
57
Đặt vấn đề
• Yêu cầu 1: Tiêu chí chọn lựa thay đổi cho đồng
bằng sông Cửu Long, không ảnh hưởng đến logic
xử lý tiêu chí lựa chọn của Tây Nguyên và ngược
lại. Ví dụ đồng bằng sông Cửu Long năm nay muốn
tăng số lượng giống cho Sầu riêng lên 50% số
giống được giao cho nông dân.
• Yêu cầu 2: Trong trường hợp khách hàng muốn mở
rộng thị trường ra nhiều khu vực khác như: Đồng
bằng sông Hồng, vùng núi Đông Bắc, vùng ngập
mặn ven biển, … Phần mềm có thể mở rộng mà
không ảnh hưởng đến logic của những khu vực
trước đó.
58
Problem
• Imagine that you’re creating a furniture shop
simulator. Your code consists of classes that
represent:
1. A family of related products, say: Chair + Sofa
+ CoffeeTable.
2. Several variants of this family. For example,
products Chair + Sofa + CoffeeTable are
available in these variants: Modern, Victorian,
ArtDeco.
59
Problem
• You need a way to create individual furniture
objects so that they match other objects of the
same family. Customers get quite mad when
they receive non-matching furniture.
• Also, you don’t want to change existing code
when adding new products or families of
products to the program. Furniture vendors
update their catalogs very often, and you
wouldn’t want to change the core code each
time it happens.
60