Monday 21 April 2014

Design pattern - Factory method pattern

1. Introduction
Factory method pattern is a reasonably simple pattern. Basically a base class is to provide public static function to return object pointer/smart pointer of its derived/concrete class or itself.

Example 1:
*********************************************************************************
class Bird{
public:
    static Bird* CreateBird(/*arugments*/);
    /*
     * public interface
     */
    virtual void Fly();
    virtual void Eat();
};

class Swift : public Bird{
};

class Eagle : public Bird{
};
*********************************************************************************

More details please read the chapter about factory method pattern in [1]. (Explanation of this code snippet) Here I would like to discuss two scenarios in my previous projects this design pattern applies.

2. Use Factory method pattern in interface class for better code de-coupling
I have discussed what interface class is in my previous blog entry, http://cpluspluslearning-petert.blogspot.co.uk/2014/04/pure-virtual-functions.html. It simply consists of pure virtual functions only and can't not be instantiated directly, and only its concrete class can be created. For better code de-coupling, simply it means that the code can be better categorized into modules and the code dependency can be reduced/minimized. In this case a factory method can be provided to instantiate the concrete objects. which will be up-casted into the interface class pointer. And the only behavior available to them is the public function declared in the interface and their detailed behavior however is defined by the overridden virtual functions in the derived classes.

Example 2:
*********************************************************************************
class Bird{
public:
    static Bird* CreateBird(/*arugments*/);
    /*
     * public interface
     */
    virtual void Fly() = 0;
    virtual void Eat() = 0;
};

class Swift : public Bird{
};

class Eagle : public Bird{
};
*********************************************************************************

Example 2 illustrates the implementation. A public static creation method is added into the interface class. "arguments" are passed into CreateBird() to create the concrete birds, like Swift and Eagle. And their individual behavior has to be re-implemented by overriding the pure virtual function.

Example 3:
*********************************************************************************
class Bird{
public:
    /*
     * public interface
     */
    virtual void Fly() = 0;
    virtual void Eat() = 0;
};

class Swift : public Bird{
};

class Eagle : public Bird{
};

Bird* CreateBird(/*arugments*/);
*********************************************************************************

Another implementation (Example 3) is to declare CreateBird() as a standalone function, which is to return a pointer or a smart pointer for Bird.

This design will provide excellent code-decoupling (simply Bird and its derived classes can be arranged into a standalone module). All the clients (who use Bird) do not need to access the implementation of Bird and any of its derived classes. The only thing needed to expose is the interface class (Bird) and the "arguments" passed into the creation method (CreateBird()). For instance in one of my previous hardware projects the arguments are read out from the EEPROM of device (it includes the device series no, model, features to support), pass these arguments to the creation method and it will create a unique model of device. But sometimes not all the initialization of  the derived classes share the common arguments. So a mutant version of factory method pattern emerges.

Example 4: - individual creation methods for concrete classes
*********************************************************************************
class Bird{
public:
    /*
     * public interface
     */
    virtual void Fly() = 0;
    virtual void Eat() = 0;
};

class Swift : public Bird{
};

class Eagle : public Bird{
};

Bird* CreateSwift(/*2 arugments*/);
Bird* CreateEagle(/*8 arugments*/);
*********************************************************************************

If not a common method can be found to create all the concrete object, each individual creation method can be added and return a pointer/smart pointer to the base class (Bird).

3. Avoid to create "class conceptually exists and does not in reality"
I have discussed CCEADNIR ("class conceptually exists and does not in reality") in my previous blog entry, http://cpluspluslearning-petert.blogspot.co.uk/2014/04/pure-virtual-functions.html. The main thing to achieve is to prevent CCEADNIR to be created. There are a few techniques to achieve.

Use pure virtual function (abstract class) + Factory method  pattern
If there exists pure virtual function in this class based on the abstraction/encapsulation, then it would be the best. Directly use the technique (add a public static creation method or add a standalone creation method) as described in Section 1.
If all the functions so far should have their default behavior, then the best candidate is the destructor. It will not bring any performance penalty and simply enforce that the base class cannot be instantiate directly. Again apply the technique, adding a creation method, as described in Section 1.

Use protected constructor of base class + Factory method pattern
Here the constructors of the base class will be declared as protected. Then only its derived classes can access it (with the assumption that no friend classes and functions).

Example 5:
*********************************************************************************
class Bird{
public:
    /*
     * public interface
     */
    virtual void Fly();
    virtual void Eat();
protected:
    // constructor not public
    Bird();
    Bird(const Bird&);
    Bird& operator=(const Bird&)
};

class Swift : public Bird{
};

class Eagle : public Bird{
};

 Bird* CreateBird(/*arugments*/);
*********************************************************************************

Add a public static creation function to instantiate the objects of the concrete class and do not provide choices (in the arguments passed into the CreateBird()) to create the base class (Bird). Or add a standalone function and do not declare it as a friend of the base class (Bird).

Both the techniques provide good solution and as well result in better code-decoupling.

Summary
Factory method pattern is one of most used design patterns in all my previous projects. All in all I think the best feature of this pattern is better code-decoupling.

Bibliography:
[1] GOF, Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, "Design Patterns - Elements of Reusable Object-Oriented Software", 1994

No comments:

Post a Comment