factory and abstract factory
DESCRIPTION
This is Class 2 on a 6 week course I taught on Software Design Patterns. This course goes over Simple Factory, Factory Method, and Abstract Factory. Class based on "Head First Design Patterns."TRANSCRIPT
Agenda
Tangent: Preventing Future Mistakes
Simple Factory
Factory Method
Abstract Factory
204/12/23
Head First Webpage
http://www.headfirstlabs.com/books/hfdp/
Contains code examples from the book in Java and C#
Forums Link for this book: http://forums.oreilly.com/category/48/Head-First-Design-
Patterns/
304/12/23
Tangent: Preventing Future MistakesImagine if we had code like this:
public class MySuperClass {
public void DoSomething() {
//step 1
//step 2
..
//step 6
}
Developer A needs to slightly change steps 2 and 6 for a certain case.
Developer creates a new class, overrides DoSomething(), and then copies and paste the entire function contents.
404/12/23
Preventing Future Mistakes (cont)
public class MySuperClass {
public void DoSomething() {
//step 1
//step 2
..
//step 5
//step 6
}
public class SubClass extends MySuperClass {
public void DoSomething() {
//step 1 Copy
//step 2 Copy with Slight Change
. . .
//step 5 Copy
//step 6 Copy with Slight Change
}504/12/23
The method DoSomething() is overwriting behavior in the superclass.
Preventing Future Mistakes (cont)Developer A leaves the company.
Developer B joins the company.
A bug is found in Step 2 of the algorithm. Developer B makes the fix in MySuperClass and the client is told that the problem has been fixed.
But months later the bug reappears in SubClass. The client is not happy.
604/12/23
Preventing Future Mistakes (cont)
If Developer A had used the Template Pattern, it would have minimized this problem from occurring.
Realities: Developer A may have been pressed for time. Developer A should have put comments in the code that refactoring
is needed. Developer A should have coordinated with Manager to indicate that
this refactoring should be added to a list and prioritized. Developer A had no idea that they were doing something wrong!
This is a good example of Technical Debt!
704/12/23
Technical Debt
804/12/23
Source: http://martinfowler.com/bliki/TechnicalDebtQuadrant.html
Review: What Pattern should we consider? We need to manage a shared network printer.
We create an algorithm for renting movies online. MoviesOnline.com and ObjectvilleMovies both use the exact same algorithm, but MoviesOnline.com would like the additional capability of displaying the next movie in the queue after a movie is rented.
You are creating a character for a computer game. You want the character to be able to support different victory dances. The computer should be able to assign different victory dances during game play.
904/12/23
Today’s Class
Word of the Day = Factory
Different Flavors Simple Factory Factory Method Abstract Factory
1004/12/23
Pizza Store in Objectville
We need a pizza store that can create pizza.
The customer will order a specific type of pizza: Cheese Veggie Greek Pepperoni etc
Each order request is for one type of pizza only.
1104/12/23
PizzaStore in Objectville During the ordering of a Pizza, we need to perform
certain actions on it: Prepare Bake Cut Box
We know that all Pizzas must perform these behaviors. In addition, we know that these behaviors will not change during runtime. (i.e. the Baking time for a Cheese Pizza will never change!)
Question: Should these behaviors (prepare, bake, etc) be represented using Inheritance or Composition?
1204/12/23
PizzaStore in Objectville (pg 112 top)
public Pizza orderPizza(String type) {
Pizza pizza = new Pizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
Creating an instance of Pizza() doesn’t make sense here because we know there are different types of Pizza.
1304/12/23
This method is responsible for creating the pizza.
It calls methods to prepare, bake, etc.
Pizza is returned to caller.
PizzaStore in Objectville (pg 112 middle)
public Pizza orderPizza(String type) {
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("pepperoni")) {
pizza = new PepperoniPizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
1404/12/23
Code that varies
A parameter indicating type
Code that stays the same
PizzaStore in Objectville (cont)
pg 113. Pressure is on for change… Now we get some new types of Pizza (Clam, Veggie)
Every time there is a change, we would need to break into this code and update the If/Else statement. (and possibly introduce bugs in our existing code).
1504/12/23
Solution? (pg 115)Move the creation of Pizzas into a separate object!
public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("pepperoni")) {
pizza = new PepperoniPizza();
} else if (type.equals("clam")) {
pizza = new ClamPizza();
} else if (type.equals("veggie")) {
pizza = new VeggiePizza();
}
return pizza;
}
}
1604/12/23
SimplePizzaFactory
Advantage: We have one place to go to add a new pizza.
Disadvantage: Whenever there is a change, we need to break into this code and add a new line. (but at least it is in one place!!)
1704/12/23
Rework of PizzaStore (pg 116)public class PizzaStore {
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
1804/12/23
Store is composed of a factory.
Creation of pizza is delegated to factory.
Simple Factory Defined (pg 117)
This is not an official pattern, but it is commonly used.
Not a bad place to start.
When people think of “Factory”, they may actually be thinking of this. Important: In a team discussion, you may need to define your
vocabulary.
1904/12/23
Change Now Occurs…
The PizzaStore is very popular and it needs to be franchised. New York is interested Chicago is interested And perhaps one day Fairfax…
Since PizzaStore is already composed of a Simple Factory, then this should be easy! Let’s just create PizzaStore with a different SimpleFactory.
2004/12/23
Example (pg 119)
//NY Pizza Factory has a different if/else logic
NYPizzaFactory nyFactory = new NYPizzaFactory();
//Create the Pizza Store, but use this Simple Factory
//instead
PizzaStore nyStore = new PizzaStore(nyFactory);
//Order pizza
nyStore.order(“Veggie”);
2104/12/23
More change happens…
New York likes the PizzaStore, but they want to add more functionality, such as schedule a delivery.
New York attempts to extend PizzaStore…
public class NYPizzaStore extends PizzaStore {
public void ScheduleDelivery() {
…
}
}
2204/12/23
More change happens… (cont)
NYPizzaFactory nyFactory = new NYPizzaFactory();
NYPizzaStore nyStore = new NYPizzaStore(nyFactory);
//Order pizza
nyStore.order(“Veggie”);
myStore.ScheduleDelivery();
New York says the following: We only have one way to create pizzas; therefore, we don’t need
to use composition for the pizza creation.
We are not happy that we have to create our extended Pizza store and create a unique factory for creating pizzas. These two classes have a one-to-one relationship with each other. Can’t they be combined??
2304/12/23
What New York wants
A framework so that NY can do the following:
Create pizzas in a NY style
Add additional functionality that is applicable to NY only.
2404/12/23
A Framework for Pizza Store (pg 120)
public abstract class PizzaStore {
abstract Pizza createPizza(String item);
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
. . .
return pizza;
}
}
2504/12/23
NOTE: We are using inheritance here to create the pizzas, not composition. Also, the constructor for PizzaStore has been removed.
NYPizzaStore (pg 123)
public class NYPizzaStore extends PizzaStore {
Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new NYStyleCheesePizza();
} else if (item.equals("veggie")) {
return new NYStyleVeggiePizza();
} else if (item.equals("clam")) {
return new NYStyleClamPizza();
} else if (item.equals("pepperoni")) {
return new NYStylePepperoniPizza();
} else return null;
}
void ScheduleDelivery();
}
2604/12/23
The subclass is defining how the pizza is created….and it is also providing unique functionality that is applicable to New York.
Factory Method
public abstract class PizzaStore {
abstract Pizza createPizza(String item);
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
. . .
return pizza;
}
}
2704/12/23
Factory Method simply sets up an interface for creating a Product (in this case, a type of Pizza). Subclasses decide which specific Product to create.
Test Drive (pg 127)
1) PizzaStore nyStore = new NYPizzaStore();
2) nyStore.orderPizza("cheese");
3)Calls createPizza(“cheese”). The NYPizzaStore version of createPizza is called. It returns a NYStyleCheesePizza();
4) orderPizza continues to call the “parts that do not vary”, such as prepare, bake, cut, box.
2804/12/23
Factory Method Defined
GoF Intent: “Defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.”
2904/12/23
Definitions (pg 134)
Product - abstraction of the object that we need to create (Pizza)
Concrete Product – implementations of a specific Product (NYStyleCheesePizza)
Creator – abstraction of the object that will create a Product (PizzaStore). Contains factory method.
Concrete Creator – implementation of a Creator that creates a specific ConcreteProduct (NYPizzaStore)
3004/12/23
IMPORTANT
The Factory Method should just return a ConcreteProduct.
3104/12/23
Lab
public abstract class PizzaStore {
abstract Pizza createPizza(String item);
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
. . .
return pizza;
}
}
3204/12/23
• Did we separate out the parts that vary from the parts that do not vary?• Did we Program to an Interface, not an Implementation?• Does the Hollywood Principle apply here?• Did we favor composition over inheritance?
Parameterized Factory Method
The example from the book is actually a Parameterized Factory Method since it can make more than one object (based on some input parameter).
abstract Pizza createPizza(String item);
It may have been a little easier if the book had started with a simpler Factory Method example…
3304/12/23
Simple Factory Method
public abstract class PizzaStore {
abstract Pizza createPizza(); //no parameter!
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
. . .
}
}
public class NYPizzaStore extends PizzaStore {
Pizza createPizza() {
return new NYStylePizza(); //just one product returned
}
}
3404/12/23
Simple Factory Method (with Default)
public abstract class PizzaStore {
void Pizza createPizza() {
return new DefaultPizza();
}
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
. . .
}
}
3504/12/23
In this example, there is default Product created (DefaultPizza).
Back to SimUDuck (pg 16)
public class MallardDuck extends Duck {
public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
}
3604/12/23
Developer would have to know to do this. The design does not enforce it.
MallardDuck is tied to two specific implementations.
Before
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck() {
}
//Not shown: setFlyBehavior, setQuackBehavior
//Not shown: abstract display method, fly, quack
}
3704/12/23
Afterpublic abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
abstract FlyBehavior createFly();
abstract QuackBehavior createQuack();
public Duck() {
flyBehavior = createFly();
quackBehavior = createQuack();
}
}
3804/12/23
Factory Methods
Now we create MallardDuck…
public class MallardDuck extends Duck {
public MallardDuck() {
}
FlyBehavior createFly() {
return FlyWithWings();
}
QuackBehavior createQuack() {
return Quack();
}
}
3904/12/23
Developer is “forced” to specify implementations of Fly and Quack.
Note that the constructor is empty.
Alternative Design: Default Behaviorpublic abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
FlyBehavior createFly() {
return DefaultFly();
}
QuackBehavior createQuack() {
return DefaultQuack();
}
public Duck() {
flyBehavior = createFly();
quackBehavior = createQuack();
}
}
4004/12/23
MallardDuck does not have to implement these methods.
Factory Method and Template
public abstract class CaffeineBeverage {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
Condiment c = createCondiment();
addCondiments(c);
}
abstract void brew();
abstract Condiment createCondiment();
void addCondiments(Condiment c) {
//add condiment c
}
}
4104/12/23
Example re-worked here
Summary
Pattern Name – Factory Method Problem – Do not want our framework to be tied to a
specific implementation of an object. Solution
Let subclass decide which implementation to use (via use of an abstract method)
Tie the Creator with a specific Product Implementation
Consequences Simple Factory Method Simple Factory Method with Default Behavior Parameterized Factory Method Factory and Template Together
4204/12/23
Changes.. Objectville Pizza HQ gets complaints that the different
franchises are not using quality ingredients. They decide that all Pizzas must be composed of the following ingredients: Dough, Sauce, Cheese, Veggies, Pepperoni, and Clams.
New York says those ingredients are fine, but they want to use their own regional ingredients for all types of pizza, for example Dough = Thin Crust Dough Sauce = Marinara Cheese = Reggiano Veggies = Garlic, Onion
4304/12/23
Pizza
public class Pizza {
Dough dough;
Sauce sauce;
Veggies veggies[];
Cheese cheese;
Pepperoni pepperoni;
Clams clam;
. . .
}
4404/12/23
Pizza becomes composed of different ingredients.
Question: How do we get these ingredients associated with a Pizza??
One idea…Constructor w/many parameters
public class Pizza {
public Pizza(Dough d, Sauce s, Cheese c, Veggies v,
Pepperoni p, Clams c) {
this.dough = d;
this.sauce = s;
this.veggies = v;
this.cheese = c;
this.pepperoni = p;
this.clam = c;
}
}
4504/12/23
Makes sense…whenever you create a pizza, you must specify these ingredients…
Rework NYPizzaStore
public class NYPizzaStore extends PizzaStore {
Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new NYStyleCheesePizza(new ThinCrustDough(),
new Marinara(), new Reggiano(), null, null );
} else if (item.equals("veggie")) {
return new NYStyleVeggiaPizza(new ThinCrustDough(),
new Marinara(), new Reggiano(), mew Garlic() , null)
}
}
4604/12/23
This will cause a lot of maintenance headaches!!! Imagine what happens when we create a new pizza! We know that we have a certain set of ingredients that are used for New York..yet we have to keep repeating that set with each constructor. Can we define this unique set just once??
PizzaIngredientFactory (pg 146)
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();
}
4704/12/23
Create an interface for creating the different ingredients of Pizza.
Note that each ingredient of Pizza has a corresponding method defined in the interface.
NYPizzaIngredientFactory (pg 147)
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough();
}
public Sauce createSauce() {
return new MarinaraSauce();
}
public Cheese createCheese() {
return new ReggianoCheese();
}
. . .
}
4804/12/23
Here we see the set of ingredients that are specific to the NY region.
Note that each time a createXXX() method is called, a new instance is returned. This will be important later on.
CheesePizza (pg 150)
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
void prepare() {
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
4904/12/23
Instead of many ingredient parameters, we just pass one.
Does this remind you of another pattern??
Creation delegated
Note: Not all ingredients have to be used!
NYPizzaStore (pg 152)public class NYPizzaStore extends PizzaStore {
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory =
new NYPizzaIngredientFactory();
if (item.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
} else if (item.equals("veggie")) {
pizza = new VeggiePizza(ingredientFactory);
} else if (item.equals("clam")) {
pizza = new ClamPizza(ingredientFactory);
} else if (item.equals("pepperoni")) {
pizza = new PepperoniPizza(ingredientFactory);
}
return pizza;
5004/12/23
The specific ingredient factory is defined once for the NY region.
Abstract Factory Defined
GoF Intent: “Provides an interface for creating families of related or dependent objects without specifying their concrete classes.”
5104/12/23
Factory Method vs. Abstract Factory
Factory Method Uses inheritance to create a Concrete Product Sub classes decide which Concrete Product to use
Abstract Factory Uses composition to create objects The objects created were a part of a family of objects. For
example, NY region had a specific set of ingredients. An abstract factory actually contains one or more Factory
Methods!
5204/12/23
Database Example
Imagine you wanted to created a database manager object as an enterprise component. This object must not be tied to a specific database implementation.
Each specific database uses different objects: Connection Command Adapter Data Reader
Oracle, SQL Server, Informix, etc will have their own version of these Sub Products.
5304/12/23
DatabaseFactory
public abstract class DatabaseFactory
{public abstract IDbCommand CreateCommand();
public abstract IDbCommand CreateCommand(string cmdText);
public abstract IDbCommand CreateCommand(string cmdText, IDbConnection cn);
public abstract IDbConnection CreateConnection();
public abstract IDbConnection CreateConnection(string cnString);
public abstract IDbDataAdapter CreateDataAdapter();
public abstract IDbDataAdapter CreateDataAdapter(IDbCommand selectCmd);
public abstract IDataReader CreateDataReader(IDbCommand dbCmd);
}
5404/12/23
Each database implementation (Oracle, SQL Server, etc) will need to create their own version of this DatabaseFactory.
The specific DatabaseFactory must be sent to the constructor of the framework class DatabaseManager.
Abstract Factory and Singleton
If you have many different Abstract Factories, you probably just need one instance of each abstract factory.
You can use a Singleton to define each Abstract Factory.
5504/12/23
A more interesting case…
What if NY Pizza Store wanted the flexibility to change their ingredients all the time??
For example, one day use: ThickCrust Dough (not Thin) Marina Sauce (which was previously used) Provolone Cheese (not Reggiano)
And then next week the cheese needs to be changed to Cheddar (since Provolone shipment didn’t come in time).
5604/12/23
What will happen??
Class Explosion!
We would need an Abstract Factory for every possible combination…and when we add a new ingredient, we could have to create even more Abstract Factories.
5704/12/23
Solution: Abstract Factory and Prototype
When we create the abstract factory, pass the different applicable products in the constructor.
PizzaIngredientFactory ingredientFactory =
new NYPizzaIngredientFactory
(new ThinCrustDough(), new MarinaraSauce(),
new Provolone() );
These products could easily come from an XML file or even by the user during runtime.
5804/12/23
Solution: Abstract Factory and Prototype
The abstract factory can then be passed to the different types of Pizzas.
When a method is called (ie, createDough, createSauce), a cloned copy of the Product is returned. This ensures that each call to a createXXX method returns a
unique instance. Note: In our standard abstract factory, a unique instance is
returned every time you call a createXXX method.
More information on Prototype http://www.oodesign.com/prototype-pattern.html Gang of Four (pg 122)
5904/12/23
Abstract Factory: Last Thoughts
This may be a hard pattern to “see” during the initial design process.
You may encounter this pattern during refactoring.
6004/12/23
Summary
Pattern Name – Abstract Factory Problem – Need to way to create a family of products Solution
Create an interface for creating products
Consequences Objects are created using interface Composition is used to create objects Abstract Factory and Singleton Abstract Factory and Prototype
6104/12/23