500 likes | 580 Views
Implementing Factory Patterns for Better Code Design and Flexibility in Object-Oriented Programming. Learn to Program to an Interface, Not an Implementation. Enhance Code Extensibility and Maintainability.
E N D
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
Being less concrete Animal sleep() makeSound() lookForFood() Dog sleep() makeSound() lookForFood() Horse sleep() makeSound() lookForFood() RHS – SOC
Being less concrete Animal oneAnimal = new Horse(); … oneAnimal.sleep(); oneAnimal.makeSound(); oneAnimal.lookForFood(): … RHS – SOC
Being less concrete Animal oneAnimal = new Dog(); … oneAnimal.sleep(); oneAnimal.makeSound(); oneAnimal.lookForFood(): … RHS – SOC
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
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
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
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
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
Being less concrete AnimalFactory Animal create(String info) Animal sleep() makeSound() lookForFood() Dog sleep() makeSound() lookForFood() Horse sleep() makeSound() lookForFood() RHS – SOC
Being less concrete publicclass AnimalFactory { public Animal create(String info) { if (info.equals(”Dog”)) return new Dog(); elseif (info.equals(”Horse”)) return new Horse(); elseif (info.equals(”Parrot”)) returnnew Parrot(); else returnnull; } } RHS – SOC
Being less concrete AnimalFactory fac; … Animal oneAnimal = fac.create(”Dog”); … oneAnimal.sleep(); oneAnimal.makeSound(); oneAnimal.lookForFood(): … RHS – SOC
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
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
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
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
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
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
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
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
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
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
Abstraction to the next level // TypoFactory code private Typo create(String type) { if (type.equals(”Heading”)) returnnew Heading(); elseif (type.equals(”Emphasis”)) returnnew Emphasis(); ... else returnnull; } RHS – SOC
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
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
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
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
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
Abstraction to the next level TypoFactory Typo create(…) Typo addText() RHS – SOC
Abstraction to the next level TypoFactory Typo TypoFactory- FormalLayout TypoFactory- SmartLayout RHS – SOC
Abstraction to the next level TypoFactory Typo TypoHeading- Formal TypoHeading- Smart TypoEmphasis- Formal TypoEmphasis- Smart RHS – SOC
Abstraction to the next level TypoHeading- Formal TypoHeading- Smart TypoFactory- FormalLayout TypoFactory- SmartLayout TypoEmphasis- Formal TypoEmphasis- Smart RHS – SOC
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
Abstraction to the next level // A configurable document creator class publicclass DocumentCreator { TypoFactory typoFac; public DocumentCreator(TypoFactory typoFac) { this.typoFac = typoFac; } public void createDocument(DocInput in) {...} } RHS – SOC
Abstraction to the next level public void createFormalDocument() { TypoFactory typoFac = new TypoFactoryFormalLayout(); DocumentCreator docCre = new DocumentCreator(typoFac); docCre.createDocument(getDocInput()); } RHS – SOC
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
The Factory method pattern Factory create() someMethod() Product ConcreteFactory create() ConcreteProduct RHS – SOC
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
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
The Abstract Factory • Answer seems simple: just use Factory Method pattern twice TypoFactory Typo GraphicFactory Graphic TypoFactory- FormalLayout TypoFactory- SmartLayout GraphicFactory- FormalLayout GraphicFactory- SmartLayout RHS – SOC
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
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
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
The Abstract Factory DocItemFactory createTypo() createGraphic() FormalDocItemFactory SmartDocItemFactory RHS – SOC
The Abstract Factory public void createFormalDocument() { DocItemFactory fac = new FormalDocItemFactory (); DocumentCreator docCre = new DocumentCreator(fac); docCre.createDocument(getDocInput()); } RHS – SOC
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
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
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
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 RHS – SOC