Define an interface for creating an
object, but let subclasses decide which class to instantiate. Factory Method
lets a class defer instantiation to subclasses.
Frameworks use abstract classes to
define and maintain relationships between objects. A framework is often
responsible for creating these objects as well.
Consider a framework for applications
that can present multiple documents to the user. Two key abstractions in this
framework are the classes Application and Document. Both classes are abstract,
and clients have to subclass them to realize their application-specific
implementations. To create a drawing application, for example, we define the
classes DrawingApplication and DrawingDocument.
The Application class is responsible for managing Documents and will create
them as required—when the user selects Open or New
from a menu, for example.
Because the particular Document
subclass to instantiate is application-specific, the Application class can't
predict the subclass of Document to instantiate—the Application class only
knows when a new document should be created, not what kind of
Document to create. This creates a dilemma: The framework must instantiate classes,
but it only knows about abstract classes, which it cannot
instantiate.
The Factory Method pattern offers a
solution. It encapsulates the knowledge of which Document subclass to create
and moves this knowledge out of the framework.
Application subclasses redefine an
abstract CreateDocument operation on Application to
return the appropriate Document subclass. Once an Application subclass is
instantiated, it can then instantiate application-specific Documents without
knowing their class. We call CreateDocument a factory
method because it's responsible for "manufacturing" an object.
Use the Factory Method pattern when
Factory methods eliminate the need
to bind application-specific classes into your code. The code only deals with
the Product interface; therefore it can work with any user-defined ConcreteProduct classes.
A potential disadvantage of factory
methods is that clients might have to subclass the Creator class just to create
a particular ConcreteProduct object. Subclassing is fine when the client has to subclass the
Creator class anyway, but otherwise the client now must deal with another point
of evolution.
Here are two additional
consequences of the Factory Method pattern:
In the Document example, the Document
class could define a factory method called CreateFileDialog
that creates a default file dialog object for opening an existing document. A
Document subclass can define an application-specific file dialog by overriding
this factory method. In this case the factory method is not abstract but
provides a reasonable default implementation.
Parallel class hierarchies result
when a class delegates some of its responsibilities to a separate class.
Consider graphical figures that can be manipulated interactively; that is, they
can be stretched, moved, or rotated using the mouse. Implementing such
interactions isn't always easy. It often requires storing and updating
information that records the state of the manipulation at a given time. This
state is needed only during manipulation; therefore it needn't be kept in the
figure object. Moreover, different figures behave differently when the user
manipulates them. For example, stretching a line figure might have the effect
of moving an endpoint, whereas stretching a text figure may change its line
spacing.
With these
constraints, it's better to use a separate Manipulator object that implements
the interaction and keeps track of any manipulation-specific state that's
needed. Different figures will use different Manipulator subclasses to handle
particular interactions. The resulting Manipulator class hierarchy parallels
(at least partially) the Figure class hierarchy:
The Figure
class provides a CreateManipulator factory method
that lets clients create a Figure's corresponding Manipulator. Figure
subclasses override this method to return an instance of the Manipulator
subclass that's right for them. Alternatively, the Figure class may implement CreateManipulator to return a default Manipulator instance,
and Figure subclasses may simply inherit that default. The Figure classes that
do so need no corresponding Manipulator subclass—hence the hierarchies are only
partially parallel.
Notice how the
factory method defines the connection between the two class hierarchies. It
localizes knowledge of which classes belong together.
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 [VL90] 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:
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;
}
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.
For example, a
subclass MyCreator could swap MyProduct and YourProduct and support a new TheirProduct subclass:
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
}
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.
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
document :=
self documentClass new.
documentClass
self subclassResponsibility
In class MyApplication we have
^ MyDocument
which returns the
class MyDocument to be instantiated to Application.
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:
public:
Product* GetProduct();
protected:
virtual
Product* CreateProduct();
private:
Product* _product;
};
Product* Creator::GetProduct
() {
if (_product
== 0) {
_product = CreateProduct();
}
return
_product;
}
5.
class Creator {
6.
public:
7.
virtual Product* CreateProduct() = 0;
8.
};
9.
10.
template <class TheProduct>
11.
class StandardCreator: public Creator {
12.
public:
13.
virtual Product* CreateProduct();
14.
};
15.
16.
template <class TheProduct>
17.
Product* StandardCreator<TheProduct>::CreateProduct () {
18.
return new TheProduct;
19.
}
With this
template, the client supplies just the product class—no subclassing
of Creator is required.
class MyProduct : public Product {
public:
MyProduct();
// ...
};
StandardCreator<MyProduct> myCreator;
The function CreateMaze (page 84) builds and returns a maze. One problem
with this function is that it hard-codes the classes of maze, rooms, doors, and
walls. We'll introduce factory methods to let subclasses choose these
components.
First we'll define factory methods
in MazeGame for creating the maze, room, wall, and door objects:
public:
Maze* CreateMaze();
// factory methods:
virtual Maze* MakeMaze() const
{ return
new Maze; }
virtual Room* MakeRoom(int n) const
{ return
new Room(n); }
virtual Wall* MakeWall() const
{ return
new Wall; }
virtual Door* MakeDoor(Room* r1, Room* r2) const
{ return
new Door(r1, r2); }
};
Each factory method returns a maze
component of a given type. MazeGame provides default implementations that return the simplest
kinds of maze, rooms, walls, and doors.
Now we can rewrite CreateMaze to use these factory methods:
Maze* MazeGame::CreateMaze () {
Maze* aMaze =
MakeMaze();
Room* r1 = MakeRoom(1);
Room* r2 = MakeRoom(2);
Door* theDoor
= MakeDoor(r1,
r2);
aMaze->AddRoom(r1);
aMaze->AddRoom(r2);
r1->SetSide(North, MakeWall());
r1->SetSide(East, theDoor);
r1->SetSide(South, MakeWall());
r1->SetSide(West, MakeWall());
r2->SetSide(North, MakeWall());
r2->SetSide(East, MakeWall());
r2->SetSide(South, MakeWall());
r2->SetSide(West, theDoor);
return aMaze;
}
Different games can subclass MazeGame to specialize parts of the maze. MazeGame subclasses can redefine some or all of the factory methods
to specify variations in products. For example, a BombedMazeGame can
redefine the Room
and Wall products to
return the bombed varieties:
class
BombedMazeGame : public MazeGame
{
public:
BombedMazeGame();
virtual Wall* MakeWall() const
{ return
new BombedWall; }
virtual Room* MakeRoom(int n) const
{ return
new RoomWithABomb(n); }
};
An EnchantedMazeGame variant
might be defined like this:
class
EnchantedMazeGame : public MazeGame
{
public:
EnchantedMazeGame();
virtual Room* MakeRoom(int n) const
{ return
new EnchantedRoom(n, CastSpell());
}
virtual Door* MakeDoor(Room* r1, Room* r2) const
{ return
new DoorNeedingSpell(r1, r2); }
protected:
Spell* CastSpell() const;
};
Factory methods pervade toolkits
and frameworks. The preceding document example is a typical use in MacApp and ET++ [WGM88].
The manipulator example is from Unidraw.
Class View in the
Smalltalk-80 Model/View/Controller framework has a method defaultController
that creates a controller, and this might appear to be a factory method [Par90]. But subclasses of View specify the class
of their default controller by defining defaultControllerClass,
which returns the class from which defaultController
creates instances. So defaultControllerClass
is the real factory method, that is, the method that subclasses should
override.
A more esoteric example in
Smalltalk-80 is the factory method parserClass defined
by Behavior (a superclass of all objects representing
classes). This enables a class to use a customized parser for its source code.
For example, a client can define a class SQLParser to
analyze the source code of a class with embedded SQL statements. The Behavior
class implements parserClass to return the standard
Smalltalk Parser class. A class that includes embedded SQL statements overrides
this method (as a class method) and returns the SQLParser
class.
The Orbix
ORB system from IONA Technologies [ION94]
uses Factory Method to generate an appropriate type of proxy (see Proxy (207))
when an object requests a reference to a remote object. Factory Method makes it
easy to replace the default proxy with one that uses client-side caching, for
example.
Abstract
Factory (87) is often implemented with factory methods. The Motivation
example in the Abstract Factory pattern illustrates Factory Method as well.
Factory methods are
usually called within Template Methods (325). In the document example
above, NewDocument is a template method.
Prototypes
(117) don't require subclassing Creator. However,
they often require an Initialize operation on the Product class. Creator uses
Initialize to initialize the object. Factory Method doesn't require such an
operation.