Consider the following issues when applying the Factory Method pattern:
The first case requires subclasses to define an implementation, because there's no reasonable default. It gets around the dilemma of having to instantiate unforeseeable classes. In the second case, the concrete Creator uses the factory method primarily for flexibility. It's following a rule that says, ``Create objects in a separate operation so that subclasses can override the way they're created.'' This rule ensures that designers of subclasses can change the class of objects their parent class instantiates if necessary.
The Unidraw graphical editing framework uses this approach for reconstructing objects saved on disk. Unidraw defines a Creator class with a factory method Create that takes a class identifier as an argument. The class identifier specifies the class to instantiate. When Unidraw saves an object to disk, it writes out the class identifier first and then its instance variables. When it reconstructs the object from disk, it reads the class identifier first.
Once the class identifier is read, the framework calls Create, passing the identifier as the parameter. Create looks up the constructor for the corresponding class and uses it to instantiate the object. Last, Create calls the object's Read operation, which reads the remaining information on the disk and initializes the object's instance variables.
A parameterized factory method has the following general form, where MyProduct and YourProduct are subclasses of Product:
Overriding a parameterized factory method lets you easily and selectively extend or change the products that a Creator produces. You can introduce new identifiers for new kinds of products, or you can associate existing identifiers with different products.class Creator { public: virtual Product* Create(ProductId); }; Product* Creator::Create (ProductId id) { if (id == MINE) return new MyProduct; if (id == YOURS) return new YourProduct; // repeat for remaining products... return 0; }
For example, a subclass MyCreator could swap MyProduct and YourProduct and support a new TheirProduct subclass:
Notice that the last thing this operation does is call Create on the parent class. That's because MyCreator::Create handles only YOURS, MINE, and THEIRS differently than the parent class. It isn't interested in other classes. Hence MyCreator extends the kinds of products created, and it defers responsibility for creating all but a few products to its parent.Product* MyCreator::Create (ProductId id) { if (id == YOURS) return new MyProduct; if (id == MINE) return new YourProduct; // N.B.: switched YOURS and MINE if (id == THEIRS) return new TheirProduct; return Creator::Create(id); // called if all others fail }
Smalltalk programs often use a method that returns the class of the object to be instantiated. A Creator factory method can use this value to create a product, and a ConcreteCreator may store or even compute this value. The result is an even later binding for the type of ConcreteProduct to be instantiated.
A Smalltalk version of the Document example can define a documentClass method on Application. The documentClass method returns the proper Document class for instantiating documents. The implementation of documentClass in MyApplication returns the MyDocument class. Thus in class Application we have
In class MyApplication we haveclientMethod document := self documentClass new. documentClass self subclassResponsibility
which returns the class MyDocument to be instantiated to Application.documentClass ^ MyDocument.
An even more flexible approach akin to parameterized factory methods is to store the class to be created as a class variable of Application. That way you don't have to subclass Application to vary the product.
Factory methods in C++ are always virtual functions and are often pure virtual. Just be careful not to call factory methods in the Creator's constructor---the factory method in the ConcreteCreator won't be available yet.
You can avoid this by being careful to access products solely through accessor operations that create the product on demand. Instead of creating the concrete product in the constructor, the constructor merely initializes it to 0. The accessor returns the product. But first it checks to make sure the product exists, and if it doesn't, the accessor creates it. This technique is sometimes called lazy initialization. The following code shows a typical implementation:
class Creator { public: Product* GetProduct(); protected: virtual Product* CreateProduct(); private: Product* _product; }; Product* Creator::GetProduct () { if (_product == 0) { _product = CreateProduct(); } return _product; }
With this template, the client supplies just the product class---no subclassing of Creator is required.class Creator { public: virtual Product* CreateProduct() = 0; }; template class StandardCreator: public Creator { public: virtual Product* CreateProduct(); }; template Product* StandardCreator ::CreateProduct () { return new TheProduct; }
class MyProduct : public Product { public: MyProduct(); // ... }; StandardCreator myCreator;