Download - Factory Patterns
Factory Patterns
RHS – SWC 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 – SWC 3
Being less concrete
Animalsleep()makeSound()lookForFood()
Dogsleep()makeSound()lookForFood()
Horsesleep()makeSound()lookForFood()
RHS – SWC 4
Being less concrete
Animal oneAnimal = new Horse();
…
oneAnimal.sleep();
oneAnimal.makeSound();
oneAnimal.lookForFood():
…
RHS – SWC 5
Being less concrete
Animal oneAnimal = new Dog();
…
oneAnimal.sleep();
oneAnimal.makeSound();
oneAnimal.lookForFood():
…
RHS – SWC 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 – SWC 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 – SWC 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 – SWC 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 – SWC 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 – SWC 11
Being less concrete
Animalsleep()makeSound()lookForFood()
Dogsleep()makeSound()lookForFood()
Horsesleep()makeSound()lookForFood()
AnimalFactoryAnimal create(String info)
RHS – SWC 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 – SWC 13
Being less concrete
AnimalFactory fac;
…
Animal oneAnimal = fac.create(”Dog”);
…
oneAnimal.sleep();
oneAnimal.makeSound();
oneAnimal.lookForFood():
…
RHS – SWC 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 – SWC 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 – SWC 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 – SWC 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 – SWC 18
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 – SWC 19
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 – SWC 20
Abstraction to the next level
• Consider a word processor:– A document is composed of various typo-
graphic 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 – SWC 21
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 – SWC 22
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 – SWC 23
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 – SWC 24
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 – SWC 25
Abstraction to the next level
• A Heading might be– Font size 24– Bold– Calibri font
• An Emphasis might be– Bold– Red font color
RHS – SWC 26
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 – SWC 27
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 – SWC 28
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 – SWC 29
Abstraction to the next level
TypoaddText()
TypoFactoryTypo create(…)
RHS – SWC 30
Abstraction to the next level
TypoTypoFactory
TypoFactory-FormalLayout
TypoFactory-SmartLayout
RHS – SWC 31
Abstraction to the next level
TypoTypoFactory
TypoHeading-Formal
TypoEmphasis-Formal
TypoHeading-Smart
TypoEmphasis-Smart
RHS – SWC 32
Abstraction to the next level
TypoHeading-Formal
TypoEmphasis-Formal
TypoHeading-Smart
TypoEmphasis-Smart
TypoFactory-FormalLayout
TypoFactory-SmartLayout
RHS – SWC 33
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 – SWC 34
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 – SWC 35
Abstraction to the next level
public void createFormalDocument()
{
TypoFactory typoFac = new TypoFactoryFormalLayout();
DocumentCreator docCre = new DocumentCreator(typoFac);
docCre.createDocument(getDocInput());
}
RHS – SWC 36
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 – SWC 37
The Factory method pattern
ProductFactorycreate()someMethod()
ConcreteFactorycreate()
ConcreteProduct
RHS – SWC 38
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 – SWC 39
The Abstract Factory
• Answer seems simple: just use Factory Method pattern twice
TypoTypoFactory
TypoFactory-FormalLayout
TypoFactory-SmartLayout
GraphicGraphicFactory
GraphicFactory-FormalLayout
GraphicFactory-SmartLayout
RHS – SWC 40
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 – SWC 41
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 – SWC 42
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 – SWC 43
The Abstract Factory
DocItemFactorycreateTypo()createGraphic()
FormalDocItemFactory SmartDocItemFactory
RHS – SWC 44
The Abstract Factory
public void createFormalDocument()
{
DocItemFactory fac = new FormalDocItemFactory();
DocumentCreator docCre = new DocumentCreator(fac);
docCre.createDocument(getDocInput());
}
RHS – SWC 45
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 – SWC 46
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 – SWC 47
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…