How to Implement the Factory Method Pattern in C++ Correctly

How to implement the factory method pattern in C++ correctly

First of all, there are cases when
object construction is a task complex
enough to justify its extraction to
another class.

I believe this point is incorrect. The complexity doesn't really matter. The relevance is what does. If an object can be constructed in one step (not like in the builder pattern), the constructor is the right place to do it. If you really need another class to perform the job, then it should be a helper class that is used from the constructor anyway.

Vec2(float x, float y);
Vec2(float angle, float magnitude); // not a valid overload!

There is an easy workaround for this:

struct Cartesian {
inline Cartesian(float x, float y): x(x), y(y) {}
float x, y;
};
struct Polar {
inline Polar(float angle, float magnitude): angle(angle), magnitude(magnitude) {}
float angle, magnitude;
};
Vec2(const Cartesian &cartesian);
Vec2(const Polar &polar);

The only disadvantage is that it looks a bit verbose:

Vec2 v2(Vec2::Cartesian(3.0f, 4.0f));

But the good thing is that you can immediately see what coordinate type you're using, and at the same time you don't have to worry about copying. If you want copying, and it's expensive (as proven by profiling, of course), you may wish to use something like Qt's shared classes to avoid copying overhead.

As for the allocation type, the main reason to use the factory pattern is usually polymorphism. Constructors can't be virtual, and even if they could, it wouldn't make much sense. When using static or stack allocation, you can't create objects in a polymorphic way because the compiler needs to know the exact size. So it works only with pointers and references. And returning a reference from a factory doesn't work too, because while an object technically can be deleted by reference, it could be rather confusing and bug-prone, see Is the practice of returning a C++ reference variable, evil? for example. So pointers are the only thing that's left, and that includes smart pointers too. In other words, factories are most useful when used with dynamic allocation, so you can do things like this:

class Abstract {
public:
virtual void do() = 0;
};

class Factory {
public:
Abstract *create();
};

Factory f;
Abstract *a = f.create();
a->do();

In other cases, factories just help to solve minor problems like those with overloads you have mentioned. It would be nice if it was possible to use them in a uniform way, but it doesn't hurt much that it is probably impossible.

Factory method C++ implementation

  1. You don't need to make instance BreadFactory. Just use static method.

    class BreadFactory
    {
    public:
    static Bread* makeBread( QString type )
    {
    ...
    }
    };

    //in main
    Bread *breadType = BreadFactory::makeBread( "White Bread");
    breadType->print();
  2. Use c++11 override keyword, for clarity.

    class WhiteBread : public Bread
    {
    public:
    void print() override{
    ...
  3. Use c++11 unique_ptr among smart pointer.

    static unique_ptr<Bread> makeBread( QString type ) {
    if ( type == "White bread" )
    return std::make_unique<WhiteBread>();
    else if ( type == "Brown bread" )
    return std::make_unique<BrownBread>();
    else
    return nullptr;
    }
  4. Use virtual destructor.

    class Bread
    {
    public:
    virtual void print() = 0;
    virtual ~Bread() {};
    };

Factory Pattern in C++ -- doing this correctly?

You do NOT want to use your interface class as the factory class. For one, if it is a true interface class, there is no implementation. Second, if the interface class does have some implementation defined (in addition to the pure virtual functions), making a static factory method now forces the base class to be recompiled every time you add a child class implementation.

The best way to implement the factory pattern is to have your interface class separate from your factory.

A very simple (and incomplete) example is below:

class MyInterface
{
public:
virtual void MyFunc() = 0;
};

class MyImplementation : public MyInterface
{
public:
virtual void MyFunc() {}
};

class MyFactory
{
public:
static MyInterface* CreateImplementation(...);
};

Factory pattern implementation using ANSI C

C have function pointers and structs. So you can implement classes in C.

something like this should give you a clue.

void class1_foo() {}
void class2_foo() {}

struct polyclass
{
void (*foo)();
};

polyclass make_class1() { polyclass result; result.foo = class1_foo; return result; }

Factory Method C++

Here is a corrected sample (as far as I understood what you want to achieved). See code comments clarifying why stuff where changed. A lot more would be needed to make the core more robust (eg using ObjectType inside the base class)

#include <iostream>

enum ObjectType {
FO_Object2 = 2,
FO_Object3 = 3
};

class Object {
public:
int ID; // ID must be public so that inherited class can access it. Otherwise create getter for ID
Object(int id) : ID(id) {};
Object(const Object&) = delete;

virtual void printObject() = 0;
static Object* make_object(int id);
};

class Object2: public Object
{
public:
Object2():Object(2) {}; // specifically instanciate a base class whose id is 2
void printObject() {
std::cout << "ID = "<< ID << std::endl;
}
};

class Object3 : public Object
{

public:
Object3():Object(3) {}; // specifically instanciate a base class whose id is 3
void printObject() {
std::cout << "ID = "<< ID << std::endl;
}
};

//Factory Method to create objects
Object* Object::make_object(int id)
{
switch (id)
{
case FO_Object2: return new Object2();
case FO_Object3: return new Object3();
default:
return NULL;
}
}

class object_factory
{
public:
object_factory()
{
ObjectType id = FO_Object3;
pObject = Object::make_object(id);
}
~object_factory()
{
if (pObject) {
delete[] pObject;
pObject = NULL;
}
}
Object* getFactory() {
return pObject;
}
private:
Object* pObject;
};

int main()
{
object_factory* pFactory = new object_factory();
Object* pObject = pFactory->getFactory();
pObject->printObject();

return 0;
}


Related Topics



Leave a reply



Submit