The Factory Method (Creational) Design Pattern
The Problem
One of the goals of object-oriented design is to delegate
responsibility among different objects. This kind of partitioning
is good since it encourages Encapsulation and Delegation.
The Solution
Factory Method is a creational pattern. This pattern helps to
model an interface for creating an object which at creation time
can let its subclasses decide which class to instantiate. We call
this a Factory Pattern since it is responsible for "Manufacturing"
an Object. It helps instantiate the appropriate Subclass by
creating the right Object from a group of related classes. The
Factory Pattern promotes loose coupling by eliminating the need
to bind application-specific classes into the code.
Factories have a simple function: Churn out objects.
Obviously, a factory is not needed to make an object. A simple call to new will do it for you. However, the use of factories gives the programmer the opportunity to abstract the specific attributes of an Object into specific subclasses which create them.
The Factory Pattern is all about "Define an interface for creating an object, but let the subclasses decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses" Thus, as defined by Gamma et al, "The Factory Method lets a class defer instantiation to subclasses".
Figure 1 below illustrates the roles of the Factory Pattern.
Figure 1: The roles of the Factory Pattern
As shown in the figure, the Factory Pattern has a couple of roles - a Creator and a Concrete Creator. This pattern is used when a class (the Creator) does not know beforehand all the subclasses that it will create. Instead, each subclass (the Concrete Creator) is left with the responsibility of creating the actual object instances.
Explanation
Example
Scenario 1 - One typical use of the Factory Pattern in a
Component Object Model (COM) application: In this scenario, the Creator
is played by a COM interface called the IClassFactory and the Concrete Creator is
played by the class which implements that interface. COM
alleviates the need for the COM runtime to know about all the
possible object types that it needs to create ahead of time.
Typically, Object creation may require acquisition of system
resources, coordination between several Objects, etc. With the
introduction of the IClassFactory
interface, all the
details of object creation can be hidden in a Concrete derivation,
manifested as a Class Factory. This way, it is only the class
factory that has to be explicitly created by the COM support
system.
Figure 2 below, shows the control flow details of component creation from the client using the Factory Pattern in a typical COM application.
Figure 2: Control Flow Details of Component Creation from the client using a Factory Pattern approach in COM
As shown in figure 2, the client first calls CoCreateInstance, which is implemented in the COM library. CoCreateInstance is implemented using CoGetClassObject. CoGetClassObject looks for the component in the Windows Registry. If it finds the component in the registry, it loads the associated DLL that serves the component. After the DLL is loaded, CoGetClassObject calls the DllGetClassObject. DllGetClassObject is implemented in the DLL Server. It's job is to create the Class Factory which it does using the new operator. DllGetClassObject then queries the Class Factory for the IClassFactory interface, which is returned to CoCreateInstance. CoCreateInstance then uses the IClassFactory interface to call it's CreateInstance function. Here, IClassFactory::CreateInstance calls the new operator to create the component. In addition, it queries for the IAccount interface. After getting the interface, CoCreateInstance releases the Class Factory and returns the IAccount interface pointer to the client. The client can now use the interface pointer to call methods on the component.
Example Scenario 2- One typical use of the Factory Pattern in the Standard Template Library (STL): In the STL, an iterator is a sort of smart pointer for which the operator*() function is overloaded in order to provide not the iterator object itself but the individual data object in the container, such as a table row. Because the iterator must have knowledge of the container internals in order to traverse the data structure, the definition of the iterator class is included with each container in the STL; Gamma et al would probably include this as an example of a Factory design pattern, in that the container, in a way, creates the iterator. In other words, the list makes its iterator. "Iterator'' itself is also a design pattern described by Gamma et al (which we will discuss in a future article).
Example Scenario 3- One typical use of the Factory Pattern in an Enterprise JavaBean (EJB) Application: An entity bean is an object representation of persistent data that are maintained in a permanent data store, such as a database. A primary key identifies each instance of an entity bean. Entity beans can be created by creating an object using an object factory Create method. Similarly, Session beans can be created by creating an object using an object factory Create method.
Figure 3 below, shows the control flow details of component creation from the client using the Factory Pattern in a typical EJB application.
Figure 3: Control Flow Details of Component Creation from the client using a Factory Pattern approach in EJB
Starting at the top left of the image, the client first creates a new context to look up the EJBObject with the help of a JNDI server. Given this context, the client then creates an EJB using the home interface. The client can then call the available methods of the EJBObject. When all the activities are complete, the client calls the remove() method, also through the home interface, to terminate the session.
The equivalent code for the same looks like Listing 1:
import javax.naming.*; public class EJBClient { public static void main (String[] argv) { // get the JNDI naming context Context initialCtx = new InitialContext (); // use the context to lookup the EJB Home interface AccountHome home=(AccountHome)initialCtx.lookup("Account"); // use the Home Interface to create a Session bean object Account account = home.create (10001, "Athul", 100000000.25d); // invoke business methods account.credit (200000000.25d); // remove the object account.remove (); } }
Listing 1: The EJB Client code to talk to an EJB
Example Scenario 4- One typical use of the Factory Pattern in the Common Object Request Broker Architecture (CORBA): The CORBA Object Services - Naming Service Specification (COSNaming), describes the entire namespace in terms of NamingContext Objects. These contexts can be connected to each other and can contain references to actual object instances for which clients will ask. The contexts are all contained within the CosNaming.Factory and CosNaming.ExtFactory servers. When the factory server is executed, it always creates a singleton persistent object implementing the CosNaming::NamingContextFactory interface. All NamingContext objects created within a particular factory server are associated with that server's single NamingContextFactory. The factories support the following interfaces as shown in Listing 2:
// CORBA Interface Definition Language module CosNaming { interface NamingContextFactory { NamingContext create_context (); oneway void shutdown (); }; interface ExtendedNamingContextFactory : NamingContextFactory { NamingContext root_context (); }; };
Listing 2: The COSNaming interface in CORBA is modelled using the Factory Pattern
Implementation
Our Example: To restrict access to authorized personnel and to prevent information glut by loading users with information that would be useless to them, lots of ecommerce applications, provide profile management by storing static and dynamic information about users. Providing individual or multiple users access to resources such as individual files, directory structures, user-defined entities (such as catalogs, purchase orders etc.) etc., can be controlled based on the individual/group's organization/role with and within an organization. Our example here, ensures information control, by providing Employees with access to Confidential Resources, while restricting the generic public to only Public Resources.
We will use this example to illustrate the different implementation options available to us.
The Classes and Interfaces used:
Figure 4: The Creator as an Abstract Class (*)
1. Resource - An abstract base class that defines a common interface to different types of Resources in an Organization. These resources may represent individual files, directory structures, user-defined entities (such as catalogs, purchase orders etc.) etc. The listings below, illustrate how the Resource class is implemented in Java and C++.
In Java,
public abstract class Resource { public abstract void printMessage (); }
Listing 3: Resource.java
In C++,
#include <stdio.h> class CResource { public: CResource() {} virtual ~CResource() {} virtual void printMessage () = 0; }; Listing 4: Resource.h
2. ConfidentialResource - A specialization of the Resource interface which represents resources which are Company Confidential resources which may not be accessed by anyone except the Company Employees.The listings below, illustrate how the ConfidentialResource class is implemented in Java and C++.
In Java,
public class ConfidentialResource extends Resource { public void printMessage () { System.out.println ("This is a Company Confidential Resource..."); } } Listing 5: ConfidentialResource.java
In C++,
#include "Resource.h" class CConfidentialResource : public CResource { public: CConfidentialResource() {} virtual ~CConfidentialResource() {} virtual void printMessage (void) { printf ("This is a Company Confidential Resource...\n"); } }; Listing 6: ConfidentialResource.h
3. PublicResource - A specialization of the Resource interface which represents resources which are available to anyone.The listings below, illustrate how the PublicResource class is implemented in Java and C++.
In Java,
public class PublicResource extends Resource { public void printMessage () { System.out.println ("This is a Public Resource..."); } } Listing 7: PublicResource.java
In C++,
#include "Resource.h" class CPublicResource : public CResource { public: CPublicResource() {} virtual ~CPublicResource() {} virtual void printMessage (void) { printf ("This is a Public Resource...\n"); } }; Listing 8: PublicResource.h
4. Profile - An abstract base class that defines a common interface to hold information about users like their Name, EMail Address, etc. A getResource() method is used to retrieve a reference to Resources. Employees have access to ConfidentialResources whereas, Non-Employees have access to PublicResources. The listings below, illustrate how the Profile class is implemented in Java and C++.
In Java,
public abstract class Profile { public Profile (String sName, String sEmail) { m_sName = sName; m_sEmail = sEmail; } public String getName () { return m_sName; } public String getEmail () { return m_sEmail; } public boolean IsEmployee () { return m_bIsEmployee; } public abstract Resource getResource (); protected String m_sName, m_sEmail; protected boolean m_bIsEmployee; } Listing 9: Profile.java
In C++,
#include <string.h> #include "Resource.h" #include "ResourceCreator.h" class CProfile { public: CProfile(char* psName, char* psEmail) { if (psName) { m_psName = new char [strlen(psName)+1]; strcpy (m_psName, psName); } if (psEmail) { m_psEmail = new char [strlen(psEmail)+1]; strcpy (m_psEmail, psEmail); } } virtual ~CProfile() { delete [] m_psName; delete [] m_psEmail; } virtual char* getName (void) { return m_psName; } virtual char* getEmail (void) { return m_psEmail; } virtual bool IsEmployee (void) { return m_bIsEmployee; } virtual CResource* getResource (void) = 0; protected: char *m_psName; char *m_psEmail; bool m_bIsEmployee; }; Listing 10: Profile.h
5. Employee - A specialization of the Profile interface represents an Object associated with an Employee of the Company. The IsEmployee() method returning a boolean value is used to test to see if the Object represents an employee of the Company.
6. NonEmployee - A specialization of the Profile interface represents an Object associated with users who are Non-Employee of the Company. The IsEmployee() method returning a boolean value is used to test to see if the Object represents an employee of the Company.
Implementation
1 - When the Creator class is an Abstract class:
This requires subclasses to
define an implementation, because there is no reasonable default.
It gets around the dialemma of having to instantiate
unforeseeable classes.
public class Employee extends Profile { public Employee (String sName, String sEmail) { super (sName, sEmail); m_bIsEmployee = true; } public Resource getResource () { return new ConfidentialResource (); } } Listing 11: Employee.java
public class NonEmployee extends Profile { public NonEmployee (String sName, String sEmail) { super (sName, sEmail); m_bIsEmployee = false; } public Resource getResource () { return new PublicResource (); } } Listing 12: NonEmployee.java
public class FactoryPattern { public static void main (String[] args) { Employee employee = new Employee ("Athul", "athul@eCommWare.com"); NonEmployee nonEmployee = new NonEmployee ("Aditya", "aditya@inventor.com"); employee.getResource ().printMessage (); nonEmployee.getResource ().printMessage (); } } Listing 13: FactoryPattern.java
Implementation
2 - When the Creator class is a Concreate class:
In this type of
implementation, the concrete Creator uses the factory
method primarily for flexibility. It says "Create objects in
a separate operation so that subclasses can change the way they
are created". This ensures that, if necessary, designers of
subclasses can change the class of objects their parent class
instantiates.
Implementation
3 - Parameterized Factory Methods:
This type of implementation
lets the factory method create multiple kinds of Objects. The
factory method takes a parameter that identifies the kind of
object to create. All Objects that the factory method creates,
share the same interface. Once the identifier is read, the
framework calls createResource
() with the
identifier passed in. The
createResource ()
method instantiates and returns the appropriate Resource reference.
public class ResourceCreator { public static final int CONFIDENTIAL = 0; public static final int PUBLIC = 1; public Resource createResource (int nID) { switch (nID) { case CONFIDENTIAL: return new ConfidentialResource (); case PUBLIC: return new PublicResource (); } return null; } } Listing 14: ResourceCreator.java
public class Employee extends Profile { public Employee (String sName, String sEmail) { super (sName, sEmail); m_bIsEmployee = true; } public Resource getResource () { ResourceCreator creator = new ResourceCreator (); return creator.createResource (ResourceCreator.CONFIDENTIAL); } } Listing 15: Employee.java
public class NonEmployee extends Profile { public NonEmployee (String sName, String sEmail) { super (sName, sEmail); m_bIsEmployee = false; } public Resource getResource () { ResourceCreator creator = new ResourceCreator (); return creator.createResource (ResourceCreator.PUBLIC); } } Listing 16: NonEmployee.java
Implementation
4 - Using Templates to avoid subclassing:
A potential problem with
factory methods is that they might force you to subclass just to
create the appropriate Objects. One way to get around this in C++
is to provide a template subclass of the Creator class
that's parameterized by the appropriate object class.
template <class aResource> class CResourceCreator { public: virtual CResource* createResource (); }; template <class aResource> CResource* CResourceCreator<aResource>::createResource () { return new aResource; } Listing 17: ResourceCreator.h
#include "Profile.h" #include "ConfidentialResource.h" class CEmployee : public CProfile { public: CEmployee (char* psName, char* psEmail) : CProfile (psName, psEmail) { m_bIsEmployee = true; } virtual ~CEmployee() {} virtual CResource* getResource (void) { CResourceCreator<CConfidentialResource> creator; return creator.createResource (); } }; Listing 18: Employee.h
#include "Profile.h" #include "PublicResource.h" class CNonEmployee : public CProfile { public: CNonEmployee (char* psName, char* psEmail) :CProfile (psName, psEmail) { m_bIsEmployee = false; } virtual ~CNonEmployee() {} virtual CResource* getResource (void) { CResourceCreator<CPublicResource> creator; return creator.createResource (); } }; Listing 19: NonEmployee.h
#include "Resource.h" #include "Employee.h" #include "NonEmployee.h" int main(int argc, char* argv[]) { CEmployee employee ("Athul", "athul@eCommWare.com"); CNonEmployee nonEmployee ("Aditya", "aditya@inventor.com"); employee.getResource ()->printMessage (); nonEmployee.getResource ()->printMessage (); return 0; } Listing 20: FactoryPattern.cpp
Checks and Balances of using this pattern
Plusses | Minus |
|
|
Also See
Abstract Factory, Template, and Prototype Patterns.
About the Author... |
Gopalan Suresh Raj is a Software Architect, Developer and an active Author. He is contributing author to a couple of books "Enterprise Java Computing-Applications and Architecture" (Cambridge University Press, June '99) and "The Awesome Power of JavaBeans" (Manning, July'98). His expertise spans enterprise component architectures and distributed object computing. Visit him at his Web Cornucopia© site (http://www.execpc.com/~gopalan) or mail him at gopalan@execpc.com. |
This site was developed and is maintained by Gopalan Suresh Raj This page has been visited times since December 27,1999. |
Last Updated : Dec27, '99 |
Copyright (c) 1997-2000, Gopalan Suresh Raj - All rights reserved. Terms of use. |
All products and companies mentioned at this site are trademarks of their respective owners. |