61
06/09/22 1 Design Patterns Week 3: Factory Method and Abstract Factory Jonathan Simon [email protected]

Factory and Abstract Factory

Embed Size (px)

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."

Citation preview

Page 1: Factory and Abstract Factory

04/12/23 1

Design Patterns

Week 3: Factory Method and Abstract Factory

Jonathan [email protected]

Page 2: Factory and Abstract Factory

Agenda

Tangent: Preventing Future Mistakes

Simple Factory

Factory Method

Abstract Factory

204/12/23

Page 3: Factory and Abstract Factory

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

Page 4: Factory and Abstract Factory

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

Page 5: Factory and Abstract Factory

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.

Page 6: Factory and Abstract Factory

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

Page 7: Factory and Abstract Factory

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

Page 8: Factory and Abstract Factory

Technical Debt

804/12/23

Source: http://martinfowler.com/bliki/TechnicalDebtQuadrant.html

Page 9: Factory and Abstract Factory

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

Page 10: Factory and Abstract Factory

Today’s Class

Word of the Day = Factory

Different Flavors Simple Factory Factory Method Abstract Factory

1004/12/23

Page 11: Factory and Abstract Factory

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

Page 12: Factory and Abstract Factory

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

Page 13: Factory and Abstract Factory

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.

Page 14: Factory and Abstract Factory

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

Page 15: Factory and Abstract Factory

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

Page 16: Factory and Abstract Factory

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

Page 17: Factory and Abstract Factory

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

Page 18: Factory and Abstract Factory

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.

Page 19: Factory and Abstract 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

Page 20: Factory and Abstract Factory

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

Page 21: Factory and Abstract Factory

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

Page 22: Factory and Abstract Factory

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

Page 23: Factory and Abstract Factory

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

Page 24: Factory and Abstract Factory

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

Page 25: Factory and Abstract Factory

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.

Page 26: Factory and Abstract Factory

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.

Page 27: Factory and Abstract Factory

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.

Page 28: Factory and Abstract Factory

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

Page 29: Factory and Abstract Factory

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

Page 30: Factory and Abstract Factory

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

Page 31: Factory and Abstract Factory

IMPORTANT

The Factory Method should just return a ConcreteProduct.

3104/12/23

Page 32: Factory and Abstract Factory

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?

Page 33: Factory and Abstract Factory

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

Page 34: Factory and Abstract Factory

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

Page 35: Factory and Abstract Factory

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).

Page 36: Factory and Abstract Factory

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.

Page 37: Factory and Abstract Factory

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

Page 38: Factory and Abstract Factory

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

Page 39: Factory and Abstract Factory

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.

Page 40: Factory and Abstract Factory

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.

Page 41: Factory and Abstract Factory

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

Page 42: Factory and Abstract Factory

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

Page 43: Factory and Abstract Factory

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

Page 44: Factory and Abstract Factory

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??

Page 45: Factory and Abstract Factory

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…

Page 46: Factory and Abstract Factory

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??

Page 47: Factory and Abstract Factory

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.

Page 48: Factory and Abstract Factory

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.

Page 49: Factory and Abstract Factory

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!

Page 50: Factory and Abstract Factory

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.

Page 51: Factory and Abstract Factory

Abstract Factory Defined

GoF Intent: “Provides an interface for creating families of related or dependent objects without specifying their concrete classes.”

5104/12/23

Page 52: Factory and Abstract Factory

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

Page 53: Factory and Abstract Factory

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

Page 54: Factory and Abstract Factory

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.

Page 55: Factory and Abstract Factory

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

Page 56: Factory and Abstract Factory

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

Page 57: Factory and Abstract Factory

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

Page 58: Factory and Abstract Factory

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

Page 59: Factory and Abstract Factory

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

Page 60: Factory and Abstract Factory

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

Page 61: Factory and Abstract Factory

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