Factory method implementation - C++

17,032

Solution 1

In the example you posted, neither a factory or a template approach makes sense to me. My solution involves a data member in the Pen class.

class Pen {
public:
    Pen() : m_color(0,0,0,0) /* the default colour is black */
    {            
    }

    Pen(const Color& c) : m_color(c)
    {
    }

    Pen(const Pen& other) : m_color(other.color())
    {
    }

    virtual void Draw()
    {
        cout << "Drawing with a pen of color " << m_color.hex();
    }
    void setColor(const Color& c) { m_color = c; }
    const Color& color() const { return m_color; }
private:
    Color m_color;
};

class Color {
public:
    Color(int r, int g, int b, int a = 0) :
        m_red(r), m_green(g), m_blue(other.blue()), m_alpha(a)  
    {
    }

    Color(const Color& other) : 
        m_red(other.red()), m_green(other.green()), 
        m_blue(other.blue()), m_alpha(other.alpha())
    {
    }

    int red() const { return m_red; }
    int green() const  { return m_green; }
    int blue() const { return m_blue; }
    int alpha() const { return m_alpha; }

    std::string hex() const
    {
        std::ostringstream os;
        char buf[3];
        os << "#";

        sprintf(buf, "%2X", red());
        os << buf;

        sprintf(buf, "%2X", green());
        os << buf;

        sprintf(buf, "%2X", blue());
        os << buf;

        sprintf(buf, "%2X", alpha());
        os << buf;

        return os.str();
    }

private:
    int m_red;
    int m_green;
    int m_blue;
    int m_alpha;
}

Of course, the color class would have to be adjusted to the drawing API you use -- and perhaps be way more advanced than this one (different color spaces, etc).

Why not templates?

The reason it does not make sense to use templates, is that (presumably) the only difference between the different drawing operations is the color variable. So, by using templates (or manually declaring different classes, as you did), you will duplicate similar code. This will make your program large, and slow it down.

So, the draw function should either take the color as an argument, or (as in my example) have the color as a class data member.

Solution 2

Another way is to dynamically register a creator function to a dynamical Factory object.

BluePen *create_BluePen() { return new BluePen; }
static bool BluePen_creator_registered = 
                       Factory::instance()->registerCreator("BluePen", 
                                                            create_BluePen);

One interesting effect in doing like this is that the static bool variable BluePen-creator-registered will be set prior main() starts thus making the registering automated.

These lines are sometimes made through ordinary macros, ie as

#define METAIMPL( _name ) \
_name *create_ ## _name() { return new _name; } \
static bool _name ## _creator_registered = \
                        Factory::instance()->registerCreator(# _name, \
                                                             create_ ## _name)

...and used close to the constructor

METAIMPL( BluePen ); // auto registers to the Factory

BluePen::BluePen() : Pen() {
   // something
}

Then the Factory's task will be to store and lookup these creator functions. I leave the rest as the exercise ;) ie the use of a METADECL macro

If you want more info, see here under chapter 4.1 Meta Information which also includes a method for expanding to include possibilities for inspector features

I learnt this from using ET++ that was a project to port old MacApp to C++ and X11. In the effort of it Eric Gamma etc started to think about Design Patterns

And...(May 7 2011) Finally came around to push an example to github
https://github.com/epatel/cpp-factory

Solution 3

Your factory is fine. I take it the BluePen and so on were just example class names. You can use templates, if the following condition is met:

When you know at compile time (i.e when you write code) that you want a specific type returned, then use a template. Otherwise, you can't.

That means in code, that you can do this:

template<typename PenType>
auto_ptr<Pen> createPen(){
    return auto_ptr<Pen>(new PenType);
}

Having that in place, you can then use that like

...
auto_ptr<Pen> p = createPen<BluePen>();
...

But that template-argument, the BluePen, can't be a variable that is set to a type at runtime. In your example, you pass a string, which can of course be set at runtime. So, when you read that you can use C++ Templates, then that recommendation is only conditionally true - then, when your decision, of which pen to create, is already done at compile time. If that condition fits, then the template solution is the right thing to do. It will not cost you anything at runtime, and will be exactly what you need.

Solution 4

By declaring special empty classes for colors, you can do everything using templates. This requires every color choice to be made available at compile-time. By doing this, you avoid having to use a base class with virtual methods.

struct Red{};
struct Blue{};

template < typename Color >
class Pen{};

template <>
class Pen< Red >
{
     void Draw(){
         cout << "Drawing with red pen" << endl;
     }
};

template <>
class Pen< Blue >
{
     void Draw(){
         cout << "Drawing with blue pen" << endl;
     }
};

template < typename Color >
std::auto_ptr< Pen< Color > > createPen()
{
     return auto_ptr< Pen< Color > >(new Pen< Color >());
}

Solution 5

You could write a generic object factory class as a template class (or use the one nicely described in this gamedev.net article).

This way, if you have more than one factory in your code, it is less work to define each factory.

Share:
17,032
Navaneeth K N
Author by

Navaneeth K N

Nothing serious about me.

Updated on June 04, 2022

Comments

  • Navaneeth K N
    Navaneeth K N almost 2 years

    I have the following code for "factory" design pattern implementation.

    class Pen{
    public:
         virtual void Draw() = 0;
    };
    
    class RedPen : public Pen{
    public:
         virtual void Draw(){
             cout << "Drawing with red pen" << endl;
         }
    };
    
    class BluePen : public Pen{
    public:
         virtual void Draw(){
             cout << "Drawing with blue pen" << endl;
         }
    };
    
    auto_ptr<Pen> createPen(const std::string color){
         if(color == "red")
             return auto_ptr<Pen>(new RedPen);
         else if(color == "blue")
             return auto_ptr<Pen>(new BluePen);
    }
    

    But I heard that it can be done in a better way using "C++ templates". Can anyone help how it is done and how template approach is better than this?

    Any thoughts

  • Benoît
    Benoît about 13 years
    What is the point of the copy-by-reference constructor in your code here ?
  • gnud
    gnud about 13 years
    That's called a copy constructor, and will get called at surprising times. As the code is written, there's no major point to it, other than to say that when I copy a pen, what I do is I copy the color of the pen. If Color was more complex, this could have mattered a lot more.