Download ppt - Factory Patterns

Transcript
Page 1: Factory Patterns

Factory Patterns

Page 2: Factory Patterns

RHS – SOC 2

Being less concrete

• One important OO principle is: ”Program to an interface, not an implementation”

• Interfaces reduces the coupling between code and concrete types

• Code does not need to know the concrete type of an object

Page 3: Factory Patterns

RHS – SOC 3

Being less concrete

Animalsleep()makeSound()lookForFood()

Dogsleep()makeSound()lookForFood()

Horsesleep()makeSound()lookForFood()

Page 4: Factory Patterns

RHS – SOC 4

Being less concrete

Animal oneAnimal = new Horse();

oneAnimal.sleep();

oneAnimal.makeSound();

oneAnimal.lookForFood():

Page 5: Factory Patterns

RHS – SOC 5

Being less concrete

Animal oneAnimal = new Dog();

oneAnimal.sleep();

oneAnimal.makeSound();

oneAnimal.lookForFood():

Page 6: Factory Patterns

RHS – SOC 6

Being less concrete

• This is fine, but we still need to be concrete when creating an object

• Also, we might need to choose – at run-time – between various concrete types

Page 7: Factory Patterns

RHS – SOC 7

Being less concrete

Animal oneAnimal;

if (needToRide)

oneAnimal = new Horse();

else if (mustBeMammal)

oneAnimal = new Dog();

else

oneAnimal = new Parrot();

oneAnimal.sleep();

oneAnimal.makeSound();

oneAnimal.lookForFood():

Page 8: Factory Patterns

RHS – SOC 8

Being less concrete

• Is anything wrong with this…?

• What if we need to add some new concrete types?

• In that case, we will need to change the code in order to include the new types

• ”Closed for modification, open for extension…”

Page 9: Factory Patterns

RHS – SOC 9

Being less concrete

• We want to isolate the references to concrete types to another class

• One class produces concrete objects, using their concrete types

• Another class processes the objects, knowing only the interface

• The processing class can then be closed for modification

Page 10: Factory Patterns

RHS – SOC 10

Being less concrete

• A class which produces objects is usually called a Factory Class

• A factory class usually has a single method: create(…)

• The create method often – but not always – takes a parameter, defining what concrete object to create

Page 11: Factory Patterns

RHS – SOC 11

Being less concrete

Animalsleep()makeSound()lookForFood()

Dogsleep()makeSound()lookForFood()

Horsesleep()makeSound()lookForFood()

AnimalFactoryAnimal create(String info)

Page 12: Factory Patterns

RHS – SOC 12

Being less concretepublic class AnimalFactory

{

public Animal create(String info)

{

if (info.equals(”Dog”))

return new Dog();

else if (info.equals(”Horse”))

return new Horse();

else if (info.equals(”Parrot”))

return new Parrot();

else

return null;

}

}

Page 13: Factory Patterns

RHS – SOC 13

Being less concrete

AnimalFactory fac;

Animal oneAnimal = fac.create(”Dog”);

oneAnimal.sleep();

oneAnimal.makeSound();

oneAnimal.lookForFood():

Page 14: Factory Patterns

RHS – SOC 14

Being less concrete

• Have I achieved something, or am I just moving code around…?

• With this setup, we can now parameterise the processing code further

• This removes the last references to concrete types

Page 15: Factory Patterns

RHS – SOC 15

Being less concrete

public void processAnAnimal(String type)

{

AnimalFactory fac = new AnimalFactory();

Animal oneAnimal = fac.create(type);

oneAnimal.sleep();

oneAnimal.makeSound();

oneAnimal.lookForFood():

}

Type specifi-cation is a parameter

Page 16: Factory Patterns

RHS – SOC 16

Being less concrete

public void processAnAnimal

(String type, AnimalFactory fac)

{

Animal oneAnimal = fac.create(type);

oneAnimal.sleep();

oneAnimal.makeSound();

oneAnimal.lookForFood():

}

Type specifi-cation and object factory are parameters

Page 17: Factory Patterns

RHS – SOC 17

Being less concrete

• This pattern is known as Simple Factory

• We have separated code for producing objects, and code for processing objects

• Processing code only knows about the interface

• Fewer responsibilities per class – ”Classes should only have one reason to change”

Page 18: Factory Patterns

RHS – SOC 18

Exercises

• Download the NetBeans project FactoryExample from the Website (go to Classes, Week 43)

• Examine the code; we have defined a Pizza interface, and three concrete pizza classes SevenSeasPizza, TorinoPizza and Vegetarian pizza

• We have also defined a base class for a PizzaStore, and two concrete pizza stores, PizzaStoreA and PizzaStoreB

• Examine the difference between PizzaStoreA and PizzaStoreB. The first one creates pizza objects directly in the code, while the second one uses a simple factory (SimplePizzaFactory)

• Try to add a new pizza type RomaPizza (remember it must implement the Pizza interface), and update the pizza factory.

• Do you need to change the code in PizzaStoreA as well?

• Do you need to change the code in PizzaStoreB as well?

Page 19: Factory Patterns

RHS – SOC 19

Abstraction to the next level

• The processing code needs a parameter which carries the type information for the object being created

• However, we also suggested that the factory itself could be a parameter

• Why would we do that….?

Page 20: Factory Patterns

RHS – SOC 20

Abstraction to the next level

public void processAnAnimal

(String type, AnimalFactory fac)

{

Animal oneAnimal = fac.create(type);

oneAnimal.sleep();

oneAnimal.makeSound();

oneAnimal.lookForFood():

}

Type specifi-cation and object factory are parameters

Page 21: Factory Patterns

RHS – SOC 21

Abstraction to the next level

• Consider a word processor:– A document is composed of various

typographic objects, like Heading, Emphasis, and so on

– All such classes implement the interface Typo– Given some input source, a piece of code

must produce a list of Typo objects

Page 22: Factory Patterns

RHS – SOC 22

Abstraction to the next level

// Part of input processing code

TypoFactory theTypoFactory;

public void createDocument(DocInput in)

{

ArrayList<Typo> doc = new ArrayList<Typo>();

while (in.hasNext())

{

TypoInput tyIn = in.next();

Typo typ = makeTypo(tyIn);

doc.add(typ);

}

}

Page 23: Factory Patterns

RHS – SOC 23

Abstraction to the next level

// Part of input processing code

private Typo makeTypo(TypoInput in)

{

String text = in.getText();

String type = in.getType();

Typo theTypo = theTypoFactory.create(type);

thetypo.addText(text);

return theTypo;

}

Page 24: Factory Patterns

RHS – SOC 24

Abstraction to the next level

// TypoFactory code

private Typo create(String type)

{

if (type.equals(”Heading”))

return new Heading();

else if (type.equals(”Emphasis”))

return new Emphasis();

...

else

return null;

}

Page 25: Factory Patterns

RHS – SOC 25

Abstraction to the next level

• The code processing the input does not know about concrete Typo classes – good

• But the code is still ”constrained”…

• What is a Typo object really – it is a ”binding” between a text and a certain way of formatting the text

• Different concrete Typo classes provide different bindings

Page 26: Factory Patterns

RHS – SOC 26

Abstraction to the next level

• A Heading might be– Font size 24– Bold– Calibri font

• An Emphasis might be– Bold– Red font color

Page 27: Factory Patterns

RHS – SOC 27

Abstraction to the next level

• A Typo factory thus defines a set of bindings between text and formatting – a layout

• What if we wish to change the layout of a document?

• We could then just define a different Typo factory, with different bindings

Page 28: Factory Patterns

RHS – SOC 28

Abstraction to the next level

// Part of input processing code

TypoFactoryFormalLayout theTypoFactory;

public void createDocument(DocInput in)

{

ArrayList<Typo> doc = new ArrayList<Typo>();

while (in.hasNext())

{

TypoInput tyIn = in.next();

Typo typ = makeTypo(tyIn);

doc.add(typ);

}

}

Just change the type of the Typo factory…

Page 29: Factory Patterns

RHS – SOC 29

Abstraction to the next level

• This solution is still quite static

• Changing to a different factory requires code modification

• Why not use interfaces once again!

• We could also define an interface for the factory side, making the processing code independent of a specific factory

Page 30: Factory Patterns

RHS – SOC 30

Abstraction to the next level

TypoaddText()

TypoFactoryTypo create(…)

Page 31: Factory Patterns

RHS – SOC 31

Abstraction to the next level

TypoTypoFactory

TypoFactory-FormalLayout

TypoFactory-SmartLayout

Page 32: Factory Patterns

RHS – SOC 32

Abstraction to the next level

TypoTypoFactory

TypoHeading-Formal

TypoEmphasis-Formal

TypoHeading-Smart

TypoEmphasis-Smart

Page 33: Factory Patterns

RHS – SOC 33

Abstraction to the next level

TypoHeading-Formal

TypoEmphasis-Formal

TypoHeading-Smart

TypoEmphasis-Smart

TypoFactory-FormalLayout

TypoFactory-SmartLayout

Page 34: Factory Patterns

RHS – SOC 34

Abstraction to the next level

• The factory for Formal layout only knows the concrete classes TypoHeading-Formal and TypoEmphasisFormal

• The factory for Smart layout only knows the concrete classes TypoHeadingSmart and TypoEmphasisSmart

• The factory interface only knows about the Typo interface

Page 35: Factory Patterns

RHS – SOC 35

Abstraction to the next level

// A configurable document creator class

public class DocumentCreator

{

TypoFactory typoFac;

public DocumentCreator(TypoFactory typoFac)

{

this.typoFac = typoFac;

}

public void createDocument(DocInput in) {...}

}

Page 36: Factory Patterns

RHS – SOC 36

Abstraction to the next level

public void createFormalDocument()

{

TypoFactory typoFac = new TypoFactoryFormalLayout();

DocumentCreator docCre = new DocumentCreator(typoFac);

docCre.createDocument(getDocInput());

}

Page 37: Factory Patterns

RHS – SOC 37

Abstraction to the next level

• Note that the only thing that changes between two TypoFactory implementa-tions is the create method

• We may include concrete methods in the Typo interface – making it an abstract class – if it makes sense

• This is known as the Factory Mehtod pattern

Page 38: Factory Patterns

RHS – SOC 38

The Factory method pattern

ProductFactorycreate()someMethod()

ConcreteFactorycreate()

ConcreteProduct

Page 39: Factory Patterns

RHS – SOC 39

Exercises• Download the NetBeans project FactoryMethodExample from the Website

(go to Classes, Week 43)

• Examine the code; we have introduced two styles of pizza; LA-style (Los Angeles), and SF-style (San Francisco), so all pizzas now come in these two variants. Corresponding pizza classes have been created

• A PizzaFactory interface has also been included, with a single method createPizza. Two concrete pizza factories have been implemented, corresponding to the two pizza styles (PizzaFactoryLAStyle and PizzaFactoryLAStyle)

• A new pizza store PizzaStoreC has been implemented. This pizza store takes a PizzaFactory object as a parameter to its constructor

• A test of the new pizza store is found in Main. Try it out! See what happens if you change the parameter to the constructor

• If time permits, try to implement a third style for pizzas, including new pizza classes and a new pizza factory class

Page 40: Factory Patterns

RHS – SOC 40

The Abstract Factory

• Our code can now work with different concrete factories, through a Factory interface

• What if we need to create several types of ”products”, not just a single type?– Typo – formattings of text– Graphic – formattings of graphic objects

Page 41: Factory Patterns

RHS – SOC 41

The Abstract Factory

• Answer seems simple: just use Factory Method pattern twice

TypoTypoFactory

TypoFactory-FormalLayout

TypoFactory-SmartLayout

GraphicGraphicFactory

GraphicFactory-FormalLayout

GraphicFactory-SmartLayout

Page 42: Factory Patterns

RHS – SOC 42

The Abstract Factory

• This looks fine…

• …but does it reflect our intention?

• Would it make sense to have a document, with – text using Formal layout– graphics using Smart layout

• Model does not include any ”binding” between related products

Page 43: Factory Patterns

RHS – SOC 43

The Abstract Factory

public void createFormalDocument()

{

TypoFactory tFac = new TypoFactoryFormalLayout();

GraphicFactory gFac = new GraphicFactorySmartLayout();

DocumentCreator docCre = new DocumentCreator(tFac,gFac);

docCre.createDocument(getDocInput());

}

Oooppss!

Page 44: Factory Patterns

RHS – SOC 44

The Abstract Factory

• A Typo and a Graphic are not – as seen from a type point-of-view – related

• Would be somewhat artificial – or perhaps even impossible – to introduce a common base class

• However, we can enforce the binding through a shared factory class!

Page 45: Factory Patterns

RHS – SOC 45

The Abstract Factory

DocItemFactorycreateTypo()createGraphic()

FormalDocItemFactory SmartDocItemFactory

Page 46: Factory Patterns

RHS – SOC 46

The Abstract Factory

public void createFormalDocument()

{

DocItemFactory fac = new FormalDocItemFactory ();

DocumentCreator docCre = new DocumentCreator(fac);

docCre.createDocument(getDocInput());

}

Page 47: Factory Patterns

RHS – SOC 47

The Abstract Factory

public void createDocument(DocInput in)

{

...

Typo aTypo = theFactory.createTypo(typoInfo);

...

Graphic aGraphic = theFactory.createGraphic(graphicInfo);

...

}

Using the same factory for creating Typo and Graphic objects!

Page 48: Factory Patterns

RHS – SOC 48

The Abstract Factory

• This pattern is known as the Abstract Factory pattern

• By making a creator class with several create… methods, we restrict the product combinations the client can create

Page 49: Factory Patterns

RHS – SOC 49

The Abstract Factory

• The methods in the Abstract Factory are product-type dependent, so if we add another product, we need to change the interface of the base class

• This is a price we must pay for binding (formally) non-related types together

• Patterns are also compromises…

Page 50: Factory Patterns

RHS – SOC 50

Exercises• Download the NetBeans project AbstractFactoryExample from the Website (go to

Classes, Week 43)• Examine the code; we have now included a Beverage as well. We assume that a

pizza is always served with a beverage. In L.A., the beverage is always cola, and in S.F., the beverage is always coffee

• Classes representing beverages have been included in the code, along with two beverage factories.

• In the new pizza store PizzaStoreD, the store is now initialised with a pizza factory and a beverage factory. See the test in Main. However, there is a problem, since we can choose to use two factories representing different styles…

• In order to fix this problem, we introduce a MealFactory interface, with two methods createPizza and createBeverage. We have also included two concrete implementations of the interface, MealFactoryLAStyle and MealFactorySFStyle.

• Inspect the implementation of the concrete meal factories and PizzaStoreE, to see how the problem of mixing factories of different styles have been eliminated

• If time permits, experiment with adding a ”side order” to a meal, like french fries, pie, ice cream, or whatever you can imagine


Recommended