04 - Factory Patterns
04 - Factory Patterns
2
What’s wrong with "new"?
• Technically there’s nothing wrong with new
• After all, it’s a fundamental part of Java
• Our problem is with CHANGE
• And how change impacts our use of new
DO YOU REMEMBER OUR FIRST DESIGN PRINCIPLE?
identify the aspects that vary and separate them from what
stays the same
▪ You know you can insulate yourself from changes by coding to an interface
▪ HOW? --> through polymorphism
▪ If you program to implementation, your code will not be "closed for modification"
▪ to extend with new behavior, you’ll have to reopen it
▪ So what can you do?
3
Identifying the aspects that vary we'll run a pizza shop today
Pizza orderPizza() {
Pizza pizza = new Pizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
4
But the pressure is on to add more pizza types
Pizza orderPizza(String type) {
Pizza pizza;
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("greek") {
pizza = new GreekPizza();
} else if (type.equals("pepperoni") {
pizza = new PepperoniPizza();
} else if (type.equals("clam")) {
pizza = new ClamPizza();
} else if (type.equals("veggie") {
pizza = new VeggiePizza();
} Clearly, dealing with which
pizza.prepare(); concrete class is instantiated is
pizza.bake(); really messing up our
pizza.cut(); orderPizza() method.
pizza.box();
return pizza; It's time to encapsulate it.
}
5
Take the creation code and
Encapsulating object creation
move it out into another
object concerned only with
creating pizzas.
6
Building a simple pizza factory
there are no
Dumb Questions
public class SimplePizzaFactory { Q: What is the advantage of this? It looks
public Pizza createPizza(String type) {
like we’re just pushing the problem off to
Pizza pizza = null;
another object?
if (type.equals("cheese")) {
pizza = new CheesePizza(); A: One thing to remember is that the
} else if (type.equals("pepperoni")) { SimplePizzaFactory may have many clients.
pizza = new PepperoniPizza();
We’ve only seen the orderPizza()
} else if (type.equals("clam")) {
pizza = new ClamPizza(); method; however, there may be a
} else if (type.equals("veggie")) { PizzaShopMenu class that uses the factory.
pizza = new VeggiePizza();
} So, by encapsulating the pizza creating in
return pizza; one class, we now have only one place to
} make modifications when the implementation
} changes.
7
Reworking the PizzaStore class
public class PizzaStore {
SimplePizzaFactory factory;
8
The Simple Factory defined
The Simple Factory isn’t
actually a Design Pattern,
it’s more of a programming
idiom.
″Implement an interface″
does NOT always mean
″write a class that
implements a Java
interface, by using the
″implements″ keyword in
the class declaration″.
In the general sense of the
phrase, a concrete class
implementing a method
from a supertype (a class
OR interface) is still to be
considered ″implementing
the interface″.
9
Franchising the pizza store
• Everyone wants our pizza in their neighborhood, so we start franchising.
• But what about regional differences (New York, Chicago, and California styles)
10
NYPizzaFactory nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
nyStore.order(“Veggie”);
return pizza;
}
12
Allowing the subclasses
decide
▪ Each subclass of PizzaStore
define what the
createPizza() method
looks like.
▪ A number of concrete
subclasses of PizzaStore,
each with its own
variations.
▪ Yet, all use our well-tuned
orderPizza() method. It is
consistent across all
franchises.
13
Well, think about it from the point of view of PizzaStore’s orderPizza()
14
Let’s make a PizzaStore
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box(); With just a couple
return pizza;
transformations to the
}
PizzaStore we’ve gone from
protected abstract Pizza createPizza(String type); having an object handle the
instantiation of our concrete
// other methods here classes to a set of subclasses
} that are now taking on that
responsibility.
16
Declaring a factory method, continued…
17
Let’s see how it works: ordering pizzas with the pizza factory method
19
We’re just missing
one thing: PIZZA !
public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();
void prepare() {
System.out.println(“Preparing “ + name);
System.out.println(“Tossing dough...”);
System.out.println(“Adding sauce...”);
System.out.println(“Adding toppings: “);
for (int i = 0; i < toppings.size(); i++) {
System.out.println(“ “ + toppings.get(i));
}
}
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”);
}
22
Meet the Factory Method Pattern
The Creator classes
! All factory patterns
encapsulate object creation
23
Another perspective: palallel class hierarchies
▪ Encapsulating knowledge into
each creator.
24
Factory Method Pattern defined
25
there are no
Dumb Questions
Q: What's the advantage of the Factory Method Pattern Q: Are the factory method and the Creator always abstract?
when you only have one ConcreteCreator? A: No, you can define a default factory method to produce
A: The Factory Method Pattern is useful if you've only got some concrete product. Then you always have a means of
one concrete creator because you are decoupling the creating products even if there are no subclasses of the
implementation of the product from its use. If you add Creator.
additional products or change a product's implementation, it
will not affect your Creator (because the Creator is not Q: Each store can make four different kinds of pizzas based
tightly coupled to any ConcreteProduct). on the type passed in. Do all concrete creators make multiple
Q: Would it be correct to say that our NY and Chicago products, or do they sometimes just make one?
stores are implemented using Simple Factory? A: We implemented what is known as the parameterized
factory method. It can make more than one object based on a
A: They're similar, but used in different ways. Even though parameter passed in, as you noticed. Often, however, a factory
the implementation of each concrete store looks a lot like just produces one object and is not parameterized. Both are
the SimplePizzaFactory, remember that the concrete stores valid forms of the pattern.
are extending a class which has defined createPizza()
as an abstract method. It is up to each store to define the
behavior of the createPizza() method. In Simple
Factory, the factory is another object that is composed with
the PizzaStore.
26
there are no
Dumb Questions
Q: Your parameterized types don't seem "type-safe." I'm Q: I'm still a bit confused about the difference between Simple
just passing in a String! What if I asked for a "CalmPizza"? Factory and Factory Method. They look very similar, except
that in Factory Method, the class that returns the pizza is a
A: You are certainly correct and that would cause, what we
subclass. Can you explain?
call in the business, a "runtime error". There are several
other more sophisticated techniques that can be used to A: You're right that the subclasses do look a lot like Simple
make parameters more "type safe", or, in other words, to Factory, however think of Simple Factory as a one shot deal,
ensure errors in parameters can be caught at compile time. while with Factory Method you are creating a framework that
For instance, you can create objects that represent the lets the subclasses decide which implementation will be used.
parameter types, use static constants, or, in Java 5, you can For example, the orderPizza() method in the Factory
use enums. Method provides a general framework for creating pizzas that
relies on a factory method to actually create the concrete
classes that go into making a pizza. By subclassing the
PizzaStore class, you decide what concrete products go into
making the pizza that orderPizza() returns. Compare that
with SimpleFactory, which gives you a way to encapsulate
object creation, but doesn't give you the flexibility of the
Factory Method because there is no way to vary the products
you're creating.
27
Master and Grasshopper
Master: Grasshopper; tell me how your training is going?
Student: Master; I have taken my study of "encapsulate what varies" further.
Master: Go on...
Student: I have learned that one can encapsulate the code that creates objects. When you have code that instantiates concrete
classes, this is an area of frequent change. I’ve learned a technique called "factories" that allows you to encapsulate this behavior
of instantiation.
Master: And these "factories, "of what benefit are they?
Student: There are many. By placing all my creation code in one object or method, I avoid duplication in my code and provide one
place to perform maintenance. That also means clients depend only upon interfaces rather than the concrete classes required to
instantiate objects. As I have learned in my studies, this allows me to program to an interface, not an implementation, and that
makes my code more flexible and extensible in the future.
Master: Yes Grasshopper, your 00 instincts are growing. Do you have any questions for your master today?
Student: Master; I know that by encapsulating object creation I am coding to abstractions and decoupling my client code from
actual implementations. But my factory code must still use concrete classes to instantiate real objects.
Master: Grasshopper; object creation is a reality of life; we must create objects or we will never create a single Java program.
But, with knowledge of this realty, we can design our code so that we have corralled this creation. Once corralled, we can protect
and care for the creation code.
Student: Master, I see the truth in this.
Master: As I knew you would. Now, please go and meditate on object dependencies.
28
public class DependentPizzaStore {
public Pizza createPizza(String style, String type) {
Pizza pizza = null;
A very dependent PizzaStore
if (style.equals("NY")) {
if (type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
} else if (type.equals("veggie")) { ▪ Let's pretend you've never heard of an
pizza = new NYStyleVeggiePizza();
} else if (type.equals("clam")) { OO factory.
pizza = new NYStyleClamPizza();
} else if (type.equals("pepperoni")) { ▪ Here's a version of the PizzaStore that
pizza = new NYStylePepperoniPizza();
} doesn't use a factory.
} else if (style.equals("Chicago")) {
if (type.equals("cheese")) { ▪ Make a count of the number of concrete
pizza = new ChicagoStyleCheesePizza();
} else if (type.equals("veggie")) { pizza objects this class is dependent
pizza = new ChicagoStyleVeggiePizza();
} else if (type.equals("clam")) { on.
pizza = new ChicagoStyleClamPizza();
} else if (type.equals("pepperoni")) { ▪ If you added California style pizzas to
pizza = new ChicagoStylePepperoniPizza();
} this PizzaStore, how many objects
} else {
System.out.println("Error: invalid type of pizza"); would it be dependent on then?
return null;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box(); 8
_______#_ 12
and _______#_ with California
return pizza;
}
}
29
Looking at object dependencies
▪ When you instantiate an
object, you are depending on
its concrete class.
▪ Take a look at our very
dependent PizzaStore one
slide back
▪ It creates all pizzas without
delegating to a factory.
30
The Dependency Inversion Principle
▪ At first, it sounds like
"Program to an interface, not
an implementation", right?
▪ They are similar, but this one
is stronger.
▪ It suggests that our high-level
components should not
depend on our low-level
components; rather they
should both depend on
abstractions. Let's think about how we'd apply this
principle to our Very Dependent
PizzaStore implementation…
31
Applying the Principle
After applying the Factory Method, our diagram looks like this:
32
Where's the "inversion" in the principle?
▪ It actually inverts your thinking about your OO
design.
▪ Look back to diagram once more and notice
that the low-level components now depend on
a higher-level abstraction.
▪ Likewise, the high-level component is also tied
to the same abstraction.
Let's also walk through the thinking behind the typical design process and see
how introducing the principle can invert the way you think about the design.
33
Inverting your thinking…
Now let's invert your thinking… instead of starting at
the top, start at the Pizzas and think about what you
can abstract.
Right, you start at top and Right! You're thinking about the abstraction Pizza. So
Ok, so you need to implement a
follow things down to the now, go back and think about the design of the Pizza
PizzaStore. What's the first
concrete class. But, as you've Store again.
thought that pops into your
seen, you don't want your
head?
store to know about the
concrete classes!
Close. But to do that you'll have to rely on a factory to get those concrete classes out of your
Pizza Store. Once you've done that, your different concrete pizza types depend only on an
abstraction and so does your store. We've taken a design where the store depended on concrete
classes and inverted those dependencies (along with your thinking).
34
A few guidelines to help you follow the Principle…
The following guidelines can help you
avoid OO designs that violate the
Dependency Inversion Principle
35
Meanwhile, back at the Pizza Store _ fresh, quality ingredient standards
We've got to do something to
ensure high quality pizzas not
to hurt Objectville brand!
36
Families of ingredients…
▪ New York uses one set of ingredients and
Chicago another. Given the popularity of
ObJectville Pizza it won't be long before you
also need to ship another set of regional
ingredients to California, and what's next?
Seattle?
37
Building the ingredient factories
▪ Now we're going to build a factory to create our ingredients;
▪ the factory will be responsible for creating each ingredient in the ingredient family.
▪ the factory will need to create dough, sauce, cheese, and so on...
Here's what we're going to do:
public interface PizzaIngredientFactory { 1. Build a factory for each region.
public Dough createDough(); To do this, we'll create a subclass of
public Sauce createSauce(); PizzaIngredientFactory that implements
public Cheese createCheese(); each create method.
public Veggies[] createVeggies(); 2. Implement a set of ingredient classes to be
public Pepperoni createPepperoni(); used with the factory, like ReggianoCheese,
public Clams createClam(); RedPeppers, and ThickCrustDough. These
} classes can be shared among regions where
appropriate.
3. Then we still need to hook all this up by
working our new ingredient factories into
our old PizzaStore code.
38
Building New York ingredient factory
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough(); ▪ Specialization of NY ingredient
}
public Sauce createSauce() { factory is
}
return new MarinaraSauce();
▪ Marinara sauce
public Cheese createCheese() { ▪ Reggiano Cheese
return new ReggianoCheese();
} ▪ Fresh Clams etc.
public Veggies[] createVeggies() {
Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
return veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clams createClam() {
return new FreshClams();
}
}
39
public abstract class Pizza {
String name;
Dough dough; Reworking the pizzas…
Sauce sauce;
Veggies veggies[]; ▪ We've got our factories all fired
Cheese cheese;
Pepperoni pepperoni; up and ready to produce quality
Clams clam;
ingredients.
abstract void prepare(); ▪ Now, we just need to rework our
void bake() { Pizzas so they only use factory-
System.out.println(“Bake for 25 minutes at 350”); produced ingredients.
}
void cut() { ▪ We'll start with our abstract
}
System.out.println(“Cutting the pizza into diagonal slices”);
Pizza class:
void box() {
System.out.println(“Place pizza in official PizzaStore box”);
}
void setName(String name) {
this.name = name;
}
String getName() {
return name;
}
public String toString() { // code to print pizza here }
}
40
Reworking the pizzas, continued…
▪ Now that we've got an abstract Pizza to work
from, it's time to create the New York and
Chicago style Pizzas
▪ only this time around they will get their public class CheesePizza extends Pizza {
ingredients straight from the factory. PizzaIngredientFactory ingredientFactory;
The franchisees' days of skimping on public CheesePizza(PizzaIngredientFactory ingredientFactory)
ingredients are over! this.ingredientFactory = ingredientFactory;
▪ When we wrote the Factory Method code, we }
had a NYCheesePizza and a void prepare() {
ChicagoCheesePizza class. If you look at the System.out.println(“Preparing “ + name);
two classes, the only thing that differs is the dough = ingredientFactory.createDough();
use of regional ingredients. The pizzas are sauce = ingredientFactory.createSauce();
made just the same (dough + sauce + cheese). cheese = ingredientFactory.createCheese();
The same goes for the other pizzas: Veggie, }
Clam, and so on. They all follow the same }
preparation steps; they just have different
ingredients. So, what you'll see is that we
really don't need two classes for each pizza;
the ingredient factory is going to handle the
regional differences for us.
41
Let's check out ClamPizza as well
public class ClamPizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public ClamPizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println(“Preparing “ + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
clam = ingredientFactory.createClam();
}
}
42
public class NYPizzaStore extends PizzaStore { Revisiting
protected Pizza createPizza(String item) {
Pizza pizza = null;
our pizza stores
PizzaIngredientFactory ingredientFactory =
new NYPizzaIngredientFactory();
if (item.equals(“cheese”)) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName(“New York Style Cheese Pizza”);
} else if (item.equals(“veggie”)) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName(“New York Style Veggie Pizza”);
} else if (item.equals(“clam”)) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName(“New York Style Clam Pizza”);
} else if (item.equals(“pepperoni”)) {
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName(“New York Style Pepperoni Pizza”);
}
return pizza;
}
}
43
What have we done? An Abstract Factory provides an interface
for a family of products. What's a family?
In our case it's all the things we need to
We provided a means of creating a make a pizza: dough, sauce, cheese, meats
and veggies.
family of ingredients for pizzas by
introducing a new type of factory
called an Abstract Factory.
An Abstract Factory gives us an interface for
creating a family of products. By writing
code that uses this interface, we decouple
our code from the actual factory that
creates the products. That allows us to
implement a variety of factories that From the abstract factory, we derive one
or more concrete factories that produce
produce products meant for different the same products, but with different
contexts - such as different regions, implementations.
44
More pizza for Ethan and Joel…
The first part of the order hasn't changed.
1. First we need a NY PizzaStore:
PizzaStore nyPizzaStore = new NYPizzaStore();
45
From here things change, we use an ingredient factory
4. When the createPizza() method is called, that's
when our ingredient factory gets involved:
Pizza pizza = new CheesePizza(nyIngredientFactory);
46
Abstract Factory Pattern defined
47
in terms of
our PizzaStore
48
Factory Method & Abstract Factory compared
49
Tools for your Design Toolbox
Bullet Points
• All factories encapsulate object creation.
• Simple Factory, while not a 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.
• All factory patterns promote loose coupling by reducing the
dependency of your application on 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.
50