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
RHS – SOC 3
Being less concrete
Animalsleep()makeSound()lookForFood()
Dogsleep()makeSound()lookForFood()
Horsesleep()makeSound()lookForFood()
RHS – SOC 4
Being less concrete
Animal oneAnimal = new Horse();
…
oneAnimal.sleep();
oneAnimal.makeSound();
oneAnimal.lookForFood():
…
RHS – SOC 5
Being less concrete
Animal oneAnimal = new Dog();
…
oneAnimal.sleep();
oneAnimal.makeSound();
oneAnimal.lookForFood():
…
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
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():
…
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…”
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
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
RHS – SOC 11
Being less concrete
Animalsleep()makeSound()lookForFood()
Dogsleep()makeSound()lookForFood()
Horsesleep()makeSound()lookForFood()
AnimalFactoryAnimal create(String info)
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;
}
}
RHS – SOC 13
Being less concrete
AnimalFactory fac;
…
Animal oneAnimal = fac.create(”Dog”);
…
oneAnimal.sleep();
oneAnimal.makeSound();
oneAnimal.lookForFood():
…
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
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
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
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”
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?
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….?
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
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
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);
}
}
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;
}
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;
}
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
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
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
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…
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
RHS – SOC 30
Abstraction to the next level
TypoaddText()
TypoFactoryTypo create(…)
RHS – SOC 31
Abstraction to the next level
TypoTypoFactory
TypoFactory-FormalLayout
TypoFactory-SmartLayout
RHS – SOC 32
Abstraction to the next level
TypoTypoFactory
TypoHeading-Formal
TypoEmphasis-Formal
TypoHeading-Smart
TypoEmphasis-Smart
RHS – SOC 33
Abstraction to the next level
TypoHeading-Formal
TypoEmphasis-Formal
TypoHeading-Smart
TypoEmphasis-Smart
TypoFactory-FormalLayout
TypoFactory-SmartLayout
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
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) {...}
}
RHS – SOC 36
Abstraction to the next level
public void createFormalDocument()
{
TypoFactory typoFac = new TypoFactoryFormalLayout();
DocumentCreator docCre = new DocumentCreator(typoFac);
docCre.createDocument(getDocInput());
}
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
RHS – SOC 38
The Factory method pattern
ProductFactorycreate()someMethod()
ConcreteFactorycreate()
ConcreteProduct
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
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
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
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
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!
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!
RHS – SOC 45
The Abstract Factory
DocItemFactorycreateTypo()createGraphic()
FormalDocItemFactory SmartDocItemFactory
RHS – SOC 46
The Abstract Factory
public void createFormalDocument()
{
DocItemFactory fac = new FormalDocItemFactory ();
DocumentCreator docCre = new DocumentCreator(fac);
docCre.createDocument(getDocInput());
}
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!
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
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…
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