Click here to Skip to main content
Click here to Skip to main content

Understanding Factory Method and Abstract Factory Patterns

By , 24 Apr 2013
Rate this:
Please Sign up or sign in to vote.

Introduction

In Design Patterns, the most commonly used and popular patterns are the Factory Method pattern and the Abstract Factory pattern. These are also the most confusing patterns for developers who are new to the world of designing.

This article explains both these ‘must know’ patterns through clear examples, and then goes on to explain the differences between them. Also, we will see the advantages and disadvantages of these two patterns.

Factory Method Pattern

According to the Gang of Four, the intent of the Factory Method is to:

“Define an interface for creating an object, but let the subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.”

Now, let us make sense of this definition for the Factory Method pattern. But first, we should discuss a problem for using a design pattern for creating objects.

During programming, we frequently come across a situation where it is required that a class needs to contain objects of other classes or class hierarchies within it. This can be very easily achieved by just using the new keyword and the class constructor. The problem with this approach is that it is a very hard coded approach to create objects as this creates dependency between the two classes.

We can use Factory Pattern to decouple the two classes. The factory methods are typically implemented as virtual methods, so this pattern is also referred to as the “Virtual Constructor”. These methods create the objects of the products or target classes.

Also, the code deals with product interfaces, and can therefore work with any user-defined concrete product classes. Thus, the code is not tightly bound to a particular application or class.

Example of code without using Factory pattern

Let us start by explaining the problem and using it as our motivation to explain the Factory method. Here, we intend to create a User Interface (UI) framework which consists of a data component, a View to display the data, and associated toolbars or sizing bars to interact with the View.

In the sample code below:

  • The class CUIFrameWork is the UI framework class.
  • The classes CDataComponent, CUIComponent, and CToolBarComponent are the products.
class CUIFrameWork{
public:
    CUITemplate* CreateUI()
    {
        CDataComponent* pData = new CDataComponent();
        CUIComponent* pUI = new CUIComponent();
 
        CToolBarComponent* pTooBar1 = new CToolBarComponent();
        CToolBarComponent* pTooBar2 = new CToolBarComponent();
        pUI->AddToolBar(pTooBar1);
        pUI->AddToolBar(pTooBar2); 
        return new CUITemplate( pData, pUI );
    }
};

Problem

Now consider adding a new UI component which needs to be a scrolling view or say a list view. A similar request for additional toolbars or sizing bars may come up. In the current approach, even if any one of the components change, we will have to write the complete code to create the objects and their binding all over again.

In the sample code below:

  • A new concrete product CUIComponentScrolling is added.
  • But now, our CUIFrameWork is aware of this new UI component as well.
class CUIFrameWork 
{
public:
    CUITemplate* CreateUI()
    {
        … Same code as in above example
    }
 
    CUITemplate* CreateScrollingUI()
    {
        CDataComponent* pData = new CDataComponent();
        CUIComponent* pUI = new CUIComponentScrolling();
    
        CToolBarComponent* pTooBar1 = new CToolBarComponent();
        CToolBarComponent* pTooBar2 = new CToolBarComponent();
        pUI->AddToolBar(pTooBar1);
        pUI->AddToolBar(pTooBar2); 
        return new CUITemplate( pData, pUI );
    }
};

Here, we cannot re-use any of the code written in the CreateUI() method. Also, the client code (CUIFrameWork) needs to be aware of or is tightly coupled with the concrete product CUIComponentScrolling.

Solution

By applying the Factory Method pattern, we now write virtual methods to create the product objects. So now, whenever a new concrete product is added and needs to be used in the CUIFrameWork, we need to derive a new framework class and create the concrete product by overriding the relevant Factory method.

Here, the main framework class (CUIFrameWork) deals only with the product interfaces, and delegates the task of creation of the concrete product to the sub-classes.

In the sample code below:

  • The Factory methods used are MakeDataComp, MakeUIComp, and MakeToolBarComp.
class CUIFrameWork 
{
public:
    // Instead of hard coding we write factory methods which
    // perform the task of object creation.
    virtual CDataComponent* MakeDataComp()
    {
        return new CDataComponent(); 
    }
 
    virtual CUIComponent* MakeUIComp()
    {
        return new CUIComponent();
    } 
    
    virtual CToolBarComponent* MakeToolBarComp( UINT nID )
    {
        return new CToolBarComponent( nID );
    } 
 
    CUITemplate* CreateUI()
    {
        CDataComponent* pData = MakeDataComp();
        CUIComponent* pUI = MakeUIComp();
        CToolBarComponent* pTooBar1 = MakeToolBarComp( ID_STANDARD );
        CToolBarComponent* pTooBar2 = MakeToolBarComp( ID_CUSTOM );
        pTooBar2->AddDropDownButton();
        pTooBar2->AddComboBox();

        pUI->AddToolBar(pTooBar1);
        pUI->AddToolBar(pTooBar2);

        return new CUITemplate( pData, pUI );
    }
};

To create a scrolling UI component, we would just need to create another framework class and override the method MakeUIComp() and create a scrolling UI component.

class CUIFrameWork_ScrollingUI : public CUIFrameWork
{
public:
    virtual CUIComponent* MakeUIComp()
    {
        return new CUIComponentScrolling();
    }
};

Similar to changing the toolbar component, we can create another framework class and override the MakeToolBarComp() method and create new toolbar components.

class CUIFrameWork_SizingBars : public CUIFrameWork
{
public:
    virtual CToolBarComponent* MakeToolBarComp( UINT nID )
    {
        return new CToolBarComponent_SizingBar( nID );
    }
};

Refer the sample client code below to create a scrolling UI.

// Client Code to create scrolling view.
CUIFrameWork_ScrollingUI objScrollUI;
CUITemplate* pTemplate = objScrollUI.CreateUI();

Advantages

  • Factory methods eliminate the need to bind application-specific classes into your code.
  • The code only deals with the product interfaces; therefore, it can work with any user-defined concrete product classes.
  • Factory methods provide hooks for sub-classes to create different concrete products. In the example below, the Factory method MakeUISpecificCtrls provides the hook for creating the UI component specific controls. In the default CUIComponent, a simple edit control is created; however, we can change this behavior in the derived class to create an edit control which accepts only floating point values.
  • Factory methods connect parallel class hierarchies in such a way that it localizes the knowledge of which classes belong together. In the example below, the CUIComponent and CEditCtrl class hierarchies can be connected with each other. Also notice how the Factory method defines the connection between the two class hierarchies.

In the sample code below:

  • The Factory method to create UI specific controls is MakeUISpecificCtrls().
  • This provides a hook so that we can derive a sub-class to create different controls to display the data.
  • Also, the Factory method connects the class hierarchies with minimum coupling.
class CUIComponent
{
public:
    virtual void MakeUISpecificCtrls()
    {
        CEditCtrl* pEdit = new CEditCtrl();
    }
};

class CUIComponent_NewView : public CUIComponent
{
public:
    void MakeUISpecificCtrls()
    {
        CEditCtrl* pEdit = new CEditNumericCtrl();
    }
};

Disadvantages

  • A potential disadvantage of Factory methods is that clients might have to sub-class the creator class just to create a particular concrete product object.
  • Subclassing is fine when the client has to subclass the creator class anyway, but otherwise, the client now must deal with another point of evolution.
  • In Factory Method pattern, the factory used for creating the objects is bound with the client code, i.e., it is difficult to use a different factory for creating objects.

Abstract Factory Pattern

According to the Gang of Four, the intent of the Abstract Factory Method is to:

“Provide an interface for creating families of related or dependent objects without specifying their concrete classes.”

An Abstract Factory is a class with several Factory methods. So basically, an Abstract Factory class has a collection of different Factory Methods to create different desired Concrete objects.

Each concrete factory sub-class implements the Factory methods for a particular family of products. The Abstract Factory pattern can be considered as an extension of the Factory Method pattern.

* How is the Abstract Factory different from the Factory Method pattern?

Factory Method

The client expects an implementation of an interface or abstract class, but doesn't know exactly what concrete class the factory will return.

Abstract Factory

Here, there is one more level of abstraction. The client does not even know what factory it's going to use. First, it gets a Factory and then it calls a Factory method. The following example will explain this.

Several examples of this pattern can be found in various well known toolkits and libraries. Let us see one of those examples before diving down and creating an Abstract Factory of our own.

Example 1

In COM, the interface IClassFactory is used to create instances of co-classes; this is an example of the Abstract Factory pattern.

Steps followed by COM to use the Class Factory:

  • The client first calls CoCreateInstance, which is implemented in the COM library. CoCreateInstance internally calls 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 DllGetClassObject. DllGetClassObject is implemented in the DLL Server.
  • DllGetClassObject will 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 its CreateInstance function.
  • Here, IClassFactory::CreateInstance calls the new operator to create the component.
  • In addition, it queries for the IUITemplate interface.
  • After getting the interface, CoCreateInstance releases the Class Factory and returns the IUITemplate interface pointer to the client.
  • The client can now use the interface pointer to call methods on the component.

Example 2

Abstract Factory Pattern generally uses composition to delegate the responsibility of object instantiation to another object; i.e., the CreateUI() method takes a reference of the Abstract Factory class.

class CFrameWorkFactory_Abs 
{
public:
    virtual CDataComponent* MakeDataComp() = 0;
    virtual CUIComponent* MakeUIComp() = 0;
    virtual CToolBarComponent* MakeToolBarComp( UINT nID ) = 0;
};

class CFrameWorkFactory : public CFrameWorkFactory_Abs 
{
    virtual CDataComponent* MakeDataComp()
    {
        return new CDataComponent(); 
    }
 
    virtual CUIComponent* MakeUIComp()
    {
        return new CUIComponent();
    } 
    
    virtual CToolBarComponent* MakeToolBarComp( UINT nID )
    {
        return new CToolBarComponent( nID );
    } 
};   

The Concrete Factory class to create the scrolling UI:

class CFrameWorkFactory_ScrollingUI : public CFrameWorkFactory
{
public:
    virtual CUIComponent* MakeUIComp()
    {
        return new CUIComponentScrolling();
    }
};

class CUIFrame 
{
public:
    CUITemplate* CreateUI(CFrameWorkFactory_Abs& objFactory)
    {
        CDataComponent* pData = objFactory.MakeDataComp();
        CUIComponent* pUI = objFactory.MakeUIComp();

        CToolBarComponent* pTooBar1 = objFactory.MakeToolBarComp( ID_STANDARD );
        CToolBarComponent* pTooBar2 = objFactory.MakeToolBarComp( ID_CUSTOM );
        pTooBar2->AddDropDownButton();
        pTooBar2->AddComboBox();

        pUI->AddToolBar(pTooBar1);
        pUI->AddToolBar(pTooBar2);

        return new CUITemplate( pData, pUI );
    }
};

So whenever we need to change the behavior of the UI framework, we need to pass the relevant Factory object. Refer the sample client code below.

Here is the sample client code to create the scrolling view:

  • Get the required Factory first.
  • Then, call the Factory methods to create the objects.
  • The client function here, CreateUI, does not know which Factory is actually used to instantiate the objects.
  • Use composition over inheritance.
CFrameWorkFactory_ScrollingUI objScrollFactory;
CUIFrame objUIFrame;
CUITemplate* pTemplate = objUIFrame.CreateUI(objScrollFactory);

Advantages

  • It isolates concrete classes from the client.
    • You use the Abstract Factory to control the classes of objects the client creates.
    • Product names are isolated in the implementation of the Concrete Factory, clients use the instances through their abstract interfaces.
  • Exchanging product families is easy.
    • None of the client code breaks because the abstract interfaces don’t change frequently.
    • Because the abstract factory creates a complete family of products, the whole product family changes when the concrete factory is changed.
  • It promotes consistency among products.
    • It is the concrete factory’s job to make sure that the right products are used together.

Disadvantages

  • Adding a new product requires extending the abstract interface which implies that all of its derived concrete classes also must change. The following changes needs to be taken care of:
    • New abstract product class is added
    • New product implementation is added
    • Abstract factory interface is extended
    • Derived concrete factories must implement the extensions
    • Client has to be extended to use the new product

How to define Extensible Factories

To overcome the above disadvantage of the Abstract Factory pattern, we can use the following approach:

We can add a parameter to the operation that creates objects. This parameter specifies the kind of object to be created. It could be a class identifier, an integer, a string, or anything else that identifies the kind of product. In fact, with this approach, Abstract Factory only needs a single "Make" operation with a parameter indicating the kind of object to create.

This is the technique used in the Prototype Pattern and the class-based abstract factories discussed earlier.

This is considered a more flexible but less safe design.

Conclusion

Creational Patterns offer great flexibility in how your software's objects are created. It is of great help to clearly understand these simple starting point patterns with their pros and cons to efficiently extend and maintain an application.

These patterns can be used as first learning step towards using other powerful but complex creational patterns.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Virendra Kulkarni
Technical Lead Persistent Systems Ltd.
India India
I am currently working as a Tech Lead for VC++ projects. My role is to Design applications and to make them extensible and very easy to maintain. I use lot of design patterns into my work to design applications. I would like to share the knowledge gained while using these patterns with everybody.

Comments and Discussions

 
GeneralMy vote of 4 PinmemberNarenderverma6-Apr-11 23:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 25 Apr 2013
Article Copyright 2009 by Virendra Kulkarni
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid