CSC/ECE 517 Fall 2012/ch2b 2w34 mr
Introduction
Abstract Factory, factory methods, builder and prototype are software creational design patterns. These design patterns deal with the the mechanism of object creation in a manner suitable to the requirement and implementation. These patterns defer part of their object creation to another object. Abstract factory pattern provides an interface for creating related objects without specifying concrete classes for the object. Factory method pattern allows a class to defer object creation to subclasses. Builder pattern separates construction of a complex object from its representation so that the same construction process can be used to create different representations. The Prototype pattern uses a prototypical instance to specify the kind of object to be created. New objects are created by cloning this prototypical instance. In this article we will discuss the Abstract Factory design pattern and compare and contrast it with the other design patterns namely, Factory method, Builder and Prototype.
Abstract Factory Pattern
Abstract Factory Pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes. A factory can be thought of as an entity responsible for creation of an object (product) without specifying the exact class the object belongs to. You may wonder how can an object be created without knowing the object’s class? The abstract factory pattern implements this with the help of interfaces or abstract classes. The client software creates a concrete implementation of the abstract factory and then uses the generic interfaces to create the concrete objects that are part of the theme. The client does not know (or care) which concrete objects it gets from each of these internal factories, since it uses only the generic interfaces of their products. This pattern separates the details of implementation of a set of objects from their general usage and relies on object composition, as object creation is implemented in methods exposed in the factory interface. The following UML representation shows how Abstract Factory pattern can be implemented.
Here, AbstractFactory declares an interface for operations that create abstract product objects. ConcreteFactory implement these operations to create concrete objects. AbstractProduct declares an interface for a type of product object. ConcreteProduct implements the AbstractProduct interface and defines a product object to be created by the corresponding concrete factory Client uses only the interfaces declared by AbstractProduct and AbstractFactory. The following Java code illustrates the implementation of AbstractFactory pattern. It demonstrates the above UML representation in code and is self explanatory.
/* GUIFactory example -- */ /* AbstractFactory */ interface Button { void paint(); } /* ConcreteFactory */ class WinButton implements Button { public void paint() { System.out.println("I'm a WinButton"); } } class OSXButton implements Button { public void paint() { System.out.println("I'm an OSXButton"); } } /* AbstractProduct */ interface GUIFactory { Button createButton(); } /* ConcreteProduct */ class WinFactory implements GUIFactory { public Button createButton() { return new WinButton(); } } class OSXFactory implements GUIFactory { public Button createButton() { return new OSXButton(); } } /* Client */ class Application { public Application(GUIFactory factory) { Button button = factory.createButton(); button.paint(); } } public class ApplicationRunner { public static void main(String[] args) { new Application(createOsSpecificFactory()); } public static GUIFactory createOsSpecificFactory() { int sys = readFromConfigFile("OS_TYPE"); if (sys == 0) return new WinFactory(); else return new OSXFactory(); } }
In this way an abstract factory can be used to create many different related objects.It can be seen that it uses subclassing (of factories) to produce other objects (non-factories). Abstract Factory also envisions that the objects produced belong to parallel hierarchies (e.g. to handle platform independance, one hierarchy for each platform) Abstract Factory classes are often implemented with Factory Methods (creation through inheritance), but they can be implemented using Prototype (creation through delegation)
Factory Method Pattern
The factory method pattern specifies an interface for creating an object but lets the class that implements the interface decide which class to instantiate. In other words it lets a class defer instantiation to subclasses. Creating an object may often require complex processes not appropriate the include within the composing object. This design pattern handles this by defining a separate method for crating the objects which subclass can then override to specify the derived type of product that will be create. Factory methods are common in toolkits and frameworks and are widely used in test-driven development (TDD).
AbstractFactory pattern using Factory Method pattern
We shall illustrate how Factory method patterns can be used to implement AbstractFactory patterns with the help of an example. Consider a program that could use several different implementations of a ResourceDescriptionFramework Database (RdfDatabase). We don't want to tie the program to any particular implementation. Two different implementations are JenaDatabase and SesameDatabase classes. Each implementation is an adapter around the third-party databases Jena and Sesame respectively. Each class also implements the RdfDatabase interface.
RdfDatabase ^ | | +-- JenaDatabase --uses---------------> Jena | | +-- SesameDatabase --uses-------------> Sesame
Here's some code explaining their relationship:
public interface RdfDatabase {} public class JenaDatabase implements RdfDatabase {} public class SesameDatabase implements RdfDatabase {}
Now consider we have a client of RdfDatabase that needs a concrete implementation to work with (i.e., to search for something in the database). Consider the following code to define this client,
public class BadClient { public Map queryDatabase(String queryString) { private RdfDatabase db = new JenaDatabase(); checkQueryIsValid(queryString); return db.find(queryString); } }
The problem with this implementation is that every time we switch implementations of RdfDatabase, we need to edit the source of a big client file and recompile it.
Here's how a FactoryMethodPattern solves the problem: A Factory Method is like a Template with the implementation of the method that creates the object left to subclasses. Say,
public abstract class TemplateClient { public Map queryDatabase(String queryString) { RdfDatabase db = databaseFactory(); checkQueryIsValid(queryString); return db.find(queryString); } public abstract RdfDatabase databaseFactory(); }
Now, in order to use the TemplateClient, we need to subclass it and provide the implementation of the appropriate database. If we needed Jena, we could use:
public class JenaClient extends TemplateClient { public RdfDatabase databaseFactory() { return new JenaDatabase(); } }
This localizes where the decision is made on which database to use. We don't risk making a mistake or introducing bugs into client code unrelated to creating databases since it is in a different class. (i.e., TemplateClient) In the above example there was a single class being used by AbstractCreator (TemplateClient). But what if we had to create twenty such classes that are dependant on each other and are highly coupled? A programmer who extends AbstractCreator would have to write twenty factory methods to keep up. Even with only a few (highly-coupled) classes, an inexperienced programmer could write methods that use two incompatible implementations. Here's an example of this problem: Instead of a single RdfDatabase, you also needed an RdfWriter that used a particular engine:
RdfDatabase ^ ^ | | | | U +-- JenaDatabase --uses---------------> Jena S | E | S +-- SesameDatabase --uses-------------> Sesame | | RdfWriter ^ | | +-- JenaWriter --uses-----------------> Jena | | +-- SesameWriter --uses---------------> Sesame
A concrete client that implements an abstract class which uses the factory method doesn't protect mistakes like:
public abstract class BadTemplate2Client { public Map writeDatabase() { RdfDatabase db = databaseFactory(); RdfWriter ww = writerFactory(); return ww.doSomethingWith(db); } public RdfDatabase databaseFactory(); public RdfWriter writerFactory(); } public class BadJena2Client extends BadTemplate2Client { public RdfDatabase databaseFactory() { return new JenaDatabase(); } public RdfWriter writerFactory() { return new SesameWriter(); } }
Whoops! That could cause lots of runtime problems if SesameWriter can't be used with JenaDatabase. However, this will compile without an error message. Here's how the AbstractFactory pattern solves this. It is a single class that just makes the highly-coupled classes:
public abstract class RdfFactory { public RdfDatabase databaseFactory(); public RdfWriter writerFactory(); }
Now that looks a lot like the bad code. However, there is a subtle improvement. You see, the programmer who writes implementations of the highly-coupled classes (JenaDatabase and JenaWriter, for instance) usually isn't writing the client which uses them. The highly-coupled classes form a framework which other client-programmers use. Well, the framework-programmer (don't confuse with the client-programmer) will write the factory class that contains the Factory Methods for each of class in the framework. Here's a revised client:
public abstract class Template2Client { public Map writeDatabase() { RdfFactory factory = createRdfFactory(); RdfDatabase db = factory.databaseFactory(); RdfWriter ww = factory.writerFactory(); return ww.doSomethingWith(db); } public RdfFactory createRdfFactory(); } public class Jena2Client extends Template2Client { public RdfFactory createRdfFactory() { return new JenaFactory(); } }
Notice: I used a factory-method to choose the factory-class to use (I named it createRdfFactory to avoid confusion with the alternative, RdfFactoryFactory). The following uses just theAbstractFactoryPattern, but is a less-flexible design:
public class Jena3Client { public Map writeDatabase() { RdfFactory factory = new JenaFactory(); RdfDatabase db = factory.databaseFactory(); RdfWriter ww = factory.writerFactory(); return ww.doSomethingWith(db); } }
Also note that the factory-class uses factory-methods to create each individual object. The functions databaseFactory() and writerFactory() are factory-methods.
Prototype Pattern
In the Prototype pattern the type of objects to create is determined by a prototypical instance which is cloned to produce new objects. This may used to avoid subclasses of an object creator in the client application, like the abstract factory pattern does or to avoid the inherent cost of creating a new object in the standard way (e.g., using the 'new' keyword) when it is prohibitively expensive for a given application. To implement the pattern, declare an abstract base class that specifies a pure virtual clone() method. Any class that needs a "polymorphic constructor" capability derives itself from the abstract base class, and implements the clone() operation The client, instead of writing code that invokes the "new" operator on a hard-coded class name, calls the clone() method on the prototype, calls a factory method with a parameterdesignating the particular concrete derived class desired, or invokes the clone() method through some mechanism provided by another design pattern Prototype doesn't require subclassing, but it does require an "initialize" operation. Factory Method requires subclassing, but doesn't require initialization.
References
http://en.wikipedia.org/wiki/Abstract_factory_pattern http://en.wikipedia.org/wiki/Factory_method http://en.wikipedia.org/wiki/Prototype_pattern