16.1.4.  Benefits of Using Factories

[ fromfile: factory.xml id: factorybenefits ]

One of the benefits of factory patterns is that we can manage the created objects in a pool (reusing the ones that can be reused).

Indirect object creation also makes it possible to decide at runtime which class objects to create. This enables the "plugging in" of replacement classes without requiring changes in the client code. Section 16.2 shows an example of a method that makes use of factory objects to create trees of connected, client-defined objects based on the contents of an XML file. In src/libs/metadata/abstractmetadataloader.h, there is another example of a factory method that manages singleton instances of MetaDataLoader in such a way that it becomes easy to write programs that can switch between derived metadata loaders without code breakage.

Another benefit of the Factory method (or indirect object creation in general) is that they can enforce post-constructor initialization of objects, including the invocation of virtual functions.

Polymorphism from Constructors

An object is not considered "fully constructed" until the constructor has finished executing. An object's vpointer does not point to the correct vtable until the end of the constructor's execution. Therefore, calls to methods of this from the constructor cannot use polymorphism!

Factory methods are required when any polymorphic behavior is needed during object initialization. Example 16.8 demonstrates this problem.

Example 16.8. src/ctorpoly/ctorpoly.cpp

#include <iostream>
using namespace std;

class A {
public:
    A() {
        cout << "in A ctor" << endl;
        foo();
    }
    virtual void foo() {
        cout << "A's foo()" << endl;
    }
};

class B: public A {
public:
    B() {
        cout << "in B ctor" << endl;
    }
    void foo() {
        cout << "B's foo()" << endl;
    }
};

class C: public B {
public:
    C() {
        cout << "in C ctor" << endl;
    }

    void foo() {
        cout << "C's foo()" << endl;
    }
};

int main() {
    C* cptr = new C;
    cout << "After construction is complete:" << endl;
    cptr->foo();
    return 0;
}


Its output is given in Example 16.9.

Example 16.9. src/ctorpoly/ctorpoly-output.txt

src/ctorpoly> ./a.out
in A ctor
A's foo()
in B ctor
in C ctor
After construction is complete:
C's foo()
src/ctorpoly>

Notice that the wrong version of foo() was called when the new C object was constructed. Section 22.1 discusses vtables in more detail.