Creational Patterns

23 min read

Overview

Creational design patterns abstract the instantiation process. They help make a system independent of how its objects are created, composed, and represented. A class creational pattern uses inheritance to vary the class that is instantiated, whereas an object creational pattern delegates instantiation to another object.

Creational patterns become important as systems evolve to depend more on object composition than class inheritance. Emphasis shifts away from hard-coding a fixed set of behaviors toward defining a smaller set of fundamental behaviors that can be composed into any number of more complex ones. Creating objects with particular behaviors then requires more than simply instantiating a class.

Two recurring themes run through these patterns. First, they all encapsulate knowledge about which concrete classes the system uses. Second, they hide how instances of those classes are created and put together. All the larger system knows about the objects is their interfaces as defined by abstract classes. Consequently, the creational patterns give you flexibility in what gets created, who creates it, how it gets created, and when. Configuration can be static (specified at compile-time) or dynamic (at run-time).

Sometimes these patterns are competitors: there are cases when either Prototype or Abstract Factory could be used profitably. At other times they are complementary: Builder can use one of the other patterns to implement which components get built, and Prototype can use Singleton in its implementation.

Key takeaways

Mental model

Mental model

The running maze example

The patterns share a common example — building a maze for a computer game — to highlight their similarities and differences. The focus is on how mazes get created, ignoring players, display, and navigation.

A maze is a set of rooms; a room knows its neighbors, which may be another room, a wall, or a door. The classes Room, Door, and Wall define the components used in all examples. Each room has four sides, given by an enumeration:

enum Direction {North, South, East, West};

MapSite is the common abstract base class for all maze components. It defines a single operation, Enter, whose meaning depends on what you are entering — entering a room changes your location; entering a closed door hurts your nose. Room is the concrete MapSite subclass holding references to its four neighboring sites and a room number. A Maze class represents the collection of rooms and can find a room by number with RoomNo. MazeGame creates the maze.

A straightforward CreateMaze member function builds a small maze of two rooms with a door between them by directly constructing each component:

Maze* MazeGame::CreateMaze () {
    Maze* aMaze = new Maze;
    Room* r1 = new Room(1);
    Room* r2 = new Room(2);
    Door* theDoor = new Door(r1, r2);

    aMaze->AddRoom(r1);
    aMaze->AddRoom(r2);

    r1->SetSide(North, new Wall);
    r1->SetSide(East, theDoor);
    r1->SetSide(South, new Wall);
    r1->SetSide(West, new Wall);

    r2->SetSide(North, new Wall);
    r2->SetSide(East, new Wall);
    r2->SetSide(South, new Wall);
    r2->SetSide(West, theDoor);

    return aMaze;
}

The real problem with this function is not its size but its inflexibility: it hard-codes the maze layout and the concrete component classes. Reusing the layout for a different game — say one with DoorNeedingSpell and EnchantedRoom — means rewriting CreateMaze. The creational patterns each remove the explicit references to concrete classes in a different way:

  • If CreateMaze calls virtual functions instead of constructor calls, a subclass of MazeGame can redefine those virtual functions to change the classes instantiated — the Factory Method pattern.
  • If CreateMaze is passed a factory object to create rooms, walls, and doors, passing a different factory changes the classes — the Abstract Factory pattern.
  • If CreateMaze is passed an object that builds the maze in its entirety through add-room, add-door, and add-wall operations, inheritance can change parts of the maze or how it is built — the Builder pattern.
  • If CreateMaze is parameterized by prototypical room, door, and wall objects that it copies, replacing the prototypes changes the maze's composition — the Prototype pattern.
  • Singleton ensures there is only one maze (or one factory) per game, accessible everywhere without global variables.

Abstract Factory

Object creational. Also known as Kit.

Intent

Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

Motivation

Consider a user interface toolkit that supports multiple look-and-feel standards, such as Motif and Presentation Manager. Different look-and-feels define different appearances and behaviors for widgets like scroll bars, windows, and buttons. To stay portable, an application should not hard-code its widgets for a particular look and feel.

The solution is an abstract WidgetFactory class that declares an interface for creating each basic kind of widget. There is an abstract class for each kind of widget, and concrete subclasses implement widgets for specific look-and-feel standards. There is a concrete subclass of WidgetFactory for each standard — MotifWidgetFactory, PMWidgetFactory — each implementing the operations to create the appropriate widget. Clients call these operations to obtain widget instances but remain unaware of the concrete classes they use, so they stay independent of the prevailing look and feel. A WidgetFactory also enforces dependencies between concrete widget classes: a Motif scroll bar is used with a Motif button automatically, as a consequence of using a MotifWidgetFactory.

Applicability

Use the Abstract Factory pattern when:

  • a system should be independent of how its products are created, composed, and represented.
  • a system should be configured with one of multiple families of products.
  • a family of related product objects is designed to be used together, and you need to enforce this constraint.
  • you want to provide a class library of products, revealing just their interfaces, not their implementations.

Structure and participants

  • AbstractFactory (WidgetFactory) — declares an interface for operations that create abstract product objects.
  • ConcreteFactory (MotifWidgetFactory, PMWidgetFactory) — implements the operations to create concrete product objects.
  • AbstractProduct (Window, ScrollBar) — declares an interface for a type of product object.
  • ConcreteProduct (MotifWindow, MotifScrollBar) — defines a product object created by the corresponding concrete factory and implements the AbstractProduct interface.
  • Client — uses only interfaces declared by AbstractFactory and AbstractProduct classes.

Collaborations: Normally a single instance of a ConcreteFactory is created at run-time; it creates products of a particular implementation. To create different products, clients use a different concrete factory. AbstractFactory defers creation of product objects to its ConcreteFactory subclass.

Consequences

  1. It isolates concrete classes. A factory encapsulates the responsibility and process of creating product objects, isolating clients from implementation classes. Product class names do not appear in client code.
  2. It makes exchanging product families easy. The class of a concrete factory appears only once — where it is instantiated. Because an abstract factory creates a complete family, the whole family changes at once when you swap the factory.
  3. It promotes consistency among products. When products in a family are designed to work together, AbstractFactory makes it easy to enforce using only one family at a time.
  4. Supporting new kinds of products is difficult. The AbstractFactory interface fixes the set of products that can be created. Adding a new kind of product requires changing the AbstractFactory class and all its subclasses.

Implementation notes

  • Factories as singletons. An application typically needs only one instance of a ConcreteFactory per product family, so it is usually best implemented as a Singleton.
  • Creating the products. AbstractFactory only declares an interface. The most common implementation defines a factory method (see Factory Method) for each product; a concrete factory specifies its products by overriding each factory method. This requires a new concrete factory subclass per family. If many families are possible, the concrete factory can instead be implemented with Prototype: it is initialized with a prototypical instance of each product and creates new products by cloning, eliminating the need for a new factory subclass per family.
  • Defining extensible factories. Adding a parameter to the creation operations — a class identifier, integer, or string indicating the kind of product — yields a more flexible but less type-safe design in which AbstractFactory needs only a single Make operation. The inherent problem is that all products return through the same abstract interface, so the client cannot safely differentiate a product's class without a downcast (for example dynamic_cast in C++), which can fail.

Sample code

MazeFactory can create components of mazes — rooms, walls, and doors. Programs that build mazes take a MazeFactory as an argument so the programmer can specify the classes to construct:

class MazeFactory {
public:
    MazeFactory();

    virtual Maze* MakeMaze() const
        { return new Maze; }
    virtual Wall* MakeWall() const
        { return new Wall; }
    virtual Room* MakeRoom(int n) const
        { return new Room(n); }
    virtual Door* MakeDoor(Room* r1, Room* r2) const
        { return new Door(r1, r2); }
};

CreateMaze is rewritten to take a MazeFactory parameter and route all construction through it:

Maze* MazeGame::CreateMaze (MazeFactory& factory) {
    Maze* aMaze = factory.MakeMaze();
    Room* r1 = factory.MakeRoom(1);
    Room* r2 = factory.MakeRoom(2);
    Door* aDoor = factory.MakeDoor(r1, r2);

    aMaze->AddRoom(r1);
    aMaze->AddRoom(r2);

    r1->SetSide(North, factory.MakeWall());
    r1->SetSide(East, aDoor);
    r1->SetSide(South, factory.MakeWall());
    r1->SetSide(West, factory.MakeWall());

    r2->SetSide(North, factory.MakeWall());
    r2->SetSide(East, factory.MakeWall());
    r2->SetSide(South, factory.MakeWall());
    r2->SetSide(West, aDoor);

    return aMaze;
}

You create EnchantedMazeFactory by subclassing MazeFactory and overriding the member functions to return different subclasses of Room, Wall, and so on. A BombedMazeFactory that yields RoomWithABomb and BombedWall need only override two functions:

Wall* BombedMazeFactory::MakeWall () const {
    return new BombedWall;
}

Room* BombedMazeFactory::MakeRoom(int n) const {
    return new RoomWithABomb(n);
}

Note that MazeFactory here is just a collection of factory methods — the most common way to implement Abstract Factory. MazeFactory is not an abstract class; it acts as both the AbstractFactory and the ConcreteFactory, a common implementation for simple applications. Building bombed mazes is then simply a matter of calling CreateMaze with a BombedMazeFactory.

AbstractFactory classes are often implemented with factory methods (Factory Method) but can also be implemented using Prototype. A concrete factory is often a Singleton.

Builder

Object creational.

Intent

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

Motivation

A reader for the RTF (Rich Text Format) document exchange format should be able to convert RTF to many text formats — plain ASCII, an interactive text widget, and so on. The number of possible conversions is open-ended, so it should be easy to add a new conversion without modifying the reader.

The solution configures the RTFReader with a TextConverter object that converts RTF tokens to another textual representation. As RTFReader parses the document, it issues a request to the TextConverter for each token it recognizes. Subclasses of TextConverter specialize in different conversions: ASCIIConverter ignores everything except plain text; TeXConverter implements all requests to capture stylistic information; TextWidgetConverter produces an editable UI object. Each converter is called a builder in the pattern, and the reader is the director. This separates the algorithm for interpreting the format (the parser) from how a converted format gets created and represented, letting you reuse the parsing algorithm to produce different representations.

Applicability

Use the Builder pattern when:

  • the algorithm for creating a complex object should be independent of the parts that make up the object and how they are assembled.
  • the construction process must allow different representations for the object that is constructed.

Structure and participants

  • Builder (TextConverter) — specifies an abstract interface for creating parts of a Product object.
  • ConcreteBuilder (ASCIIConverter, TeXConverter, TextWidgetConverter) — constructs and assembles parts of the product by implementing the Builder interface; defines and keeps track of the representation it creates; and provides an interface for retrieving the product (for example GetASCIIText, GetTextWidget).
  • Director (RTFReader) — constructs an object using the Builder interface.
  • Product (ASCIIText, TeXText, TextWidget) — represents the complex object under construction, including its constituent parts and the interfaces for assembling them into the final result.

Collaborations: The client creates the Director and configures it with the desired Builder. The Director notifies the builder whenever a part of the product should be built. The builder handles requests from the director and adds parts to the product. The client retrieves the product from the builder.

Consequences

  1. It lets you vary a product's internal representation. The Builder object hides the representation and internal structure of the product, and how it is assembled. To change the representation, you define a new kind of builder.
  2. It isolates code for construction and representation. Each ConcreteBuilder contains all the code to create and assemble one kind of product, written once; different Directors can reuse it. In the RTF example, an SGMLReader could reuse the same converters.
  3. It gives you finer control over the construction process. Unlike patterns that construct products in one shot, Builder constructs the product step by step under the director's control, and the director retrieves the product only when it is finished.

Implementation notes

There is typically an abstract Builder class with an operation for each component a director may ask it to create; the operations do nothing by default, and a ConcreteBuilder overrides only those it cares about. Key issues: the assembly/construction interface must be general enough for all concrete builders (a model that appends results to the product is usually sufficient, but sometimes you need access to earlier parts — as in tree structures built bottom-up). Often there is no abstract class for products, because products differ so greatly in representation that a common parent gains little. In C++, the build methods are intentionally not pure virtual but defined as empty methods, so clients override only the operations they want.

Sample code

The MazeBuilder class defines an interface for building mazes that can create three things — the maze, rooms with a particular number, and doors between numbered rooms — plus a GetMaze operation to return the result:

class MazeBuilder {
public:
    virtual void BuildMaze() { }
    virtual void BuildRoom(int room) { }
    virtual void BuildDoor(int roomFrom, int roomTo) { }

    virtual Maze* GetMaze() { return 0; }
protected:
    MazeBuilder();
};

The maze-building operations do nothing by default and are not pure virtual, so derived classes override only the methods they want. CreateMaze is changed to take this builder as a parameter; note how the builder hides the internal representation of the maze — there is no hint of a Wall class:

Maze* MazeGame::CreateMaze (MazeBuilder& builder) {
    builder.BuildMaze();

    builder.BuildRoom(1);
    builder.BuildRoom(2);
    builder.BuildDoor(1, 2);

    return builder.GetMaze();
}

MazeBuilder does not create mazes itself; its purpose is to define an interface, with empty implementations for convenience. The subclass StandardMazeBuilder builds simple mazes, tracking the maze under construction in _currentMaze:

class StandardMazeBuilder : public MazeBuilder {
public:
    StandardMazeBuilder();

    virtual void BuildMaze();
    virtual void BuildRoom(int);
    virtual void BuildDoor(int, int);

    virtual Maze* GetMaze();
private:
    Direction CommonWall(Room*, Room*);
    Maze* _currentMaze;
};

BuildMaze instantiates a Maze; BuildRoom creates a room and builds the walls around it; BuildDoor looks up both rooms and finds their adjoining wall. A more exotic builder, CountingMazeBuilder, creates no maze at all — it just counts the kinds of components that would have been created, demonstrating that the same construction protocol can drive very different representations.

Abstract Factory is similar in that it too may construct complex objects, but Builder focuses on constructing a complex object step by step and returns the product as a final step, whereas Abstract Factory emphasizes families of products that get returned immediately. A Composite is what the builder often builds.

Factory Method

Class creational. Also known as Virtual Constructor.

Intent

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Motivation

Frameworks use abstract classes to define and maintain relationships between objects, and are often responsible for creating those objects. Consider a framework with abstract classes Application and Document; clients subclass them — DrawingApplication, DrawingDocument. The Application knows when a new document should be created but not what kind. This creates a dilemma: the framework must instantiate classes but only knows abstract ones.

Factory Method solves this by having Application subclasses redefine an abstract CreateDocument operation that returns the appropriate Document subclass. CreateDocument is a factory method because it is responsible for "manufacturing" an object.

Applicability

Use the Factory Method pattern when:

  • a class can't anticipate the class of objects it must create.
  • a class wants its subclasses to specify the objects it creates.
  • classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate.

Structure and participants

  • Product (Document) — defines the interface of objects the factory method creates.
  • ConcreteProduct (MyDocument) — implements the Product interface.
  • Creator (Application) — declares the factory method, which returns an object of type Product; may define a default implementation that returns a default ConcreteProduct, and may call the factory method itself.
  • ConcreteCreator (MyApplication) — overrides the factory method to return an instance of a ConcreteProduct.

Collaborations: Creator relies on its subclasses to define the factory method so that it returns an instance of the appropriate ConcreteProduct.

Consequences

Factory methods eliminate the need to bind application-specific classes into your code — the code deals only with the Product interface, so it works with any user-defined ConcreteProduct. A potential disadvantage is that clients might have to subclass the Creator just to create a particular ConcreteProduct. Two further consequences:

  1. Provides hooks for subclasses. Creating objects with a factory method is more flexible than creating them directly; it gives subclasses a hook for an extended version of an object (for example a Document subclass overriding CreateFileDialog).
  2. Connects parallel class hierarchies. When a class delegates responsibilities to a separate class — for example a Figure and its Manipulator — a factory method like CreateManipulator defines and localizes the connection: each Figure subclass returns the right Manipulator subclass.

Implementation notes

  • Two major varieties. Either the Creator is abstract and provides no implementation for the factory method (subclasses must define it), or the Creator is concrete and provides a default implementation (used mainly for flexibility).
  • Parameterized factory methods. A factory method can take a parameter identifying the kind of product to create; all products share the Product interface. The Unidraw framework uses this to reconstruct objects from disk via a Create operation taking a class identifier. The general form, where MyProduct and YourProduct are subclasses of Product:
Product* Creator::Create (ProductId id) {
    if (id == MINE)  return new MyProduct;
    if (id == YOURS) return new YourProduct;
    // repeat for remaining products...

    return 0;
}

A subclass extends the products by overriding Create, handling a few ids itself and deferring the rest to the 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
}
  • Language-specific variants. Smalltalk programs often use a method that returns the class of the object to instantiate, producing an even later binding. In C++, factory methods are always virtual and often pure virtual; do not call a factory method in the Creator's constructor, because the ConcreteCreator's version is not yet available. A common workaround is lazy initialization — the constructor initializes the product pointer to 0, and an accessor creates the product on demand:
class Creator {
public:
    Product* GetProduct();
protected:
    virtual Product* CreateProduct();
private:
    Product* _product;
};

Product* Creator::GetProduct () {
    if (_product == 0) {
        _product = CreateProduct();
    }
    return _product;
}
  • Using templates to avoid subclassing. A template subclass of Creator parameterized by the Product class lets the client supply just the product class — no subclassing of Creator required.
  • Naming conventions. Use names that make the factory method obvious; MacApp declares the abstract factory operation as Class* DoMakeClass().

Sample code

Factory methods are introduced into MazeGame for creating the maze, rooms, walls, and doors, with default implementations returning the simplest kinds:

class MazeGame {
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); }
};

CreateMaze is rewritten to call these factory methods instead of constructors. Different games subclass MazeGame and redefine some or all of the factory methods. A BombedMazeGame redefines the room and wall products:

Wall* BombedMazeGame::MakeWall () const {
    return new BombedWall;
}

Room* BombedMazeGame::MakeRoom(int n) const {
    return new RoomWithABomb(n);
}

Abstract Factory is often implemented with factory methods. Factory methods are usually called within Template Methods. Prototype does not require subclassing Creator, but it often requires an Initialize operation on the product; Factory Method does not.

Prototype

Object creational.

Intent

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

Motivation

You could build an editor for music scores by customizing a general framework for graphical editors. The framework provides an abstract Graphic class and an abstract Tool class, plus a GraphicTool subclass for tools that create graphical objects. But GraphicTool belongs to the framework and does not know how to create instances of application-specific music classes such as notes and staves. Subclassing GraphicTool for each kind of music object would produce many subclasses that differ only in the object they instantiate.

The solution is to make GraphicTool create a new Graphic by cloning an instance of a Graphic subclass — the prototype. Each tool is a GraphicTool initialized with a different prototype, and it produces objects by cloning that prototype. Prototype can reduce the number of classes dramatically: rather than separate whole-note and half-note classes, both can be instances of the same class initialized with different bitmaps and durations.

Applicability

Use the Prototype pattern when a system should be independent of how its products are created, composed, and represented; and:

  • when the classes to instantiate are specified at run-time, for example by dynamic loading; or
  • to avoid building a class hierarchy of factories that parallels the class hierarchy of products; or
  • when instances of a class can have one of only a few different combinations of state — it may be more convenient to install a corresponding number of prototypes and clone them than to instantiate the class manually each time.

Structure and participants

  • Prototype (Graphic) — declares an interface for cloning itself.
  • ConcretePrototype (Staff, WholeNote, HalfNote) — implements an operation for cloning itself.
  • Client (GraphicTool) — creates a new object by asking a prototype to clone itself.

Collaborations: A client asks a prototype to clone itself.

Consequences

Prototype shares many consequences with Abstract Factory and Builder: it hides the concrete product classes from the client, reducing the number of names clients know, and lets clients work with application-specific classes without modification. Additional benefits:

  1. Adding and removing products at run-time. A new product class is incorporated simply by registering a prototypical instance; clients can install and remove prototypes at run-time.
  2. Specifying new objects by varying values. You define new "classes" without programming by instantiating existing classes and registering them as prototypes. One GraphicTool class can create a limitless variety of music objects.
  3. Specifying new objects by varying structure. Applications that build objects from parts — circuit editors building circuits from subcircuits — can add a composite object as a prototype, provided Clone is implemented as a deep copy.
  4. Reduced subclassing. Cloning a prototype instead of asking a factory method to make a new object avoids the parallel Creator hierarchy that Factory Method produces. This benefits languages like C++ that don't treat classes as first-class objects.
  5. Configuring an application with classes dynamically. Prototype is the key to exploiting run-time class loading in a language like C++: when a class is loaded, the run-time creates an instance and registers it with a prototype manager.

The main liability is that each subclass of Prototype must implement Clone, which can be difficult — especially when the classes already exist, or when their internals include objects that don't support copying or have circular references.

Implementation notes

  • Using a prototype manager. When the set of prototypes is not fixed, keep a registry — a prototype manager — that is an associative store returning the prototype matching a given key, with operations to register and unregister prototypes. Clients can extend the system without writing code.
  • Implementing the Clone operation. The hardest part is implementing Clone correctly, particularly with circular references. Most languages provide some cloning support (Smalltalk's copy, C++'s copy constructor), but these do not resolve the shallow copy versus deep copy problem. A shallow copy shares pointers; cloning prototypes with complex structures usually requires a deep copy so the clone and original are independent. If objects provide Save and Load, you can implement a default Clone by saving to a memory buffer and immediately loading back.
  • Initializing clones. Some clients want to initialize internal state after cloning. You generally cannot pass these values in Clone (their number varies between prototype classes), so introduce a separate Initialize operation that takes the parameters and sets the clone's state. Beware of deep-copying clones — the existing copies may need to be deleted before reinitializing.

Sample code

MazePrototypeFactory subclasses MazeFactory and is initialized with prototypes of the objects it creates, so you need not subclass it just to change the classes of walls or rooms:

class MazePrototypeFactory : public MazeFactory {
public:
    MazePrototypeFactory(Maze*, Wall*, Room*, Door*);

    virtual Maze* MakeMaze() const;
    virtual Room* MakeRoom(int) const;
    virtual Wall* MakeWall() const;
    virtual Door* MakeDoor(Room*, Room*) const;
private:
    Maze* _prototypeMaze;
    Room* _prototypeRoom;
    Wall* _prototypeWall;
    Door* _prototypeDoor;
};

The constructor initializes its prototypes, and each Make member function clones a prototype and then initializes it. For example:

Wall* MazePrototypeFactory::MakeWall () const {
    return _prototypeWall->Clone();
}

Door* MazePrototypeFactory::MakeDoor (Room* r1, Room* r2) const {
    Door* door = _prototypeDoor->Clone();
    door->Initialize(r1, r2);
    return door;
}

A default maze is created by initializing the factory with basic component prototypes; to change the type of maze, you initialize it with a different set of prototypes — for example a BombedDoor and a RoomWithABomb. An object used as a prototype, such as a Wall, must support Clone and have a copy constructor. The BombedWall subclass overrides Clone and implements a corresponding copy constructor:

class BombedWall : public Wall {
public:
    BombedWall();
    BombedWall(const BombedWall&);

    virtual Wall* Clone() const;
    bool HasBomb();
private:
    bool _bomb;
};

Wall* BombedWall::Clone () const {
    return new BombedWall(*this);
}

Although BombedWall::Clone returns a Wall*, its implementation returns a pointer to a new BombedWall. Defining Clone to return the base type in the base class ensures clients never need to downcast the return value.

Prototype and Abstract Factory are competing patterns in some ways but can be used together — an Abstract Factory might store a set of prototypes from which to clone and return products. Designs that make heavy use of Composite and Decorator often benefit from Prototype as well.

Singleton

Object creational.

Intent

Ensure a class only has one instance, and provide a global point of access to it.

Motivation

It is important for some classes to have exactly one instance. There may be many printers in a system, but only one printer spooler; only one file system; only one window manager. A global variable makes an object accessible but does not prevent instantiating multiple objects. A better solution makes the class itself responsible for keeping track of its sole instance — it can ensure no other instance is created (by intercepting requests to create new objects) and provide a way to access the instance. This is the Singleton pattern.

Applicability

Use the Singleton pattern when:

  • there must be exactly one instance of a class, and it must be accessible to clients from a well-known access point.
  • the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code.

Structure and participants

  • Singleton — defines an Instance operation that lets clients access its unique instance. Instance is a class operation (a static member function in C++, a class method in Smalltalk). The Singleton may be responsible for creating its own unique instance.

Collaborations: Clients access a Singleton instance solely through Singleton's Instance operation.

Consequences

  1. Controlled access to sole instance. Because the class encapsulates its sole instance, it has strict control over how and when clients access it.
  2. Reduced name space. It is an improvement over global variables; it avoids polluting the namespace with globals that store sole instances.
  3. Permits refinement of operations and representation. The Singleton class may be subclassed, and it is easy to configure an application with an instance of the extended class at run-time.
  4. Permits a variable number of instances. It is easy to change your mind and allow more than one instance; only the operation that grants access needs to change.
  5. More flexible than class operations. Packaging the functionality in static member functions or class methods makes it hard to allow more than one instance later, and static member functions in C++ are never virtual, so subclasses cannot override them polymorphically.

Implementation notes

  • Ensuring a unique instance. Hide the operation that creates the instance behind a class operation that guarantees only one instance is created. In C++, define a static member function Instance and a static member variable _instance holding a pointer to the unique instance. The constructor is protected, so a client trying to instantiate directly gets a compile-time error:
class Singleton {
public:
    static Singleton* Instance();
protected:
    Singleton();
private:
    static Singleton* _instance;
};

The implementation uses lazy initialization — the instance is not created and stored until first accessed:

Singleton* Singleton::_instance = 0;

Singleton* Singleton::Instance () {
    if (_instance == 0) {
        _instance = new Singleton;
    }
    return _instance;
}

Because _instance is a pointer to a Singleton, Instance can assign a pointer to a subclass of Singleton. It is not enough to define the singleton as a global or static object and rely on automatic initialization, for three reasons: (a) you can't guarantee only one instance of a static object will ever be declared; (b) you might lack the information to instantiate every singleton at static initialization time; and (c) C++ does not define the order in which constructors for global objects are called across translation units, so no dependencies can exist between singletons.

  • Subclassing the Singleton class. The main issue is installing the subclass instance so clients use it. The simplest technique determines which singleton to use inside the Instance operation, for example via an environment variable. The most flexible approach uses a registry of singletons: instead of Instance knowing all possible Singleton classes, the classes register their instance by name in a well-known registry, and Instance consults the registry. Singleton classes can register themselves in their constructor — but the constructor will not run unless someone instantiates the class, which can be solved in C++ by defining a static instance in the file containing the subclass's implementation:
static MySingleton theSingleton;

Sample code

Making MazeFactory a Singleton makes the maze factory globally accessible without global variables. Add a static Instance operation and a static _instance member, and protect the constructor:

class MazeFactory {
public:
    static MazeFactory* Instance();

    // existing interface goes here
protected:
    MazeFactory();
private:
    static MazeFactory* _instance;
};

When subclasses of MazeFactory exist and the application must choose one, Instance is a good place to select the subclass — for example via an environment variable:

MazeFactory* MazeFactory::_instance = 0;

MazeFactory* MazeFactory::Instance () {
    if (_instance == 0) {
        const char* mazeStyle = getenv("MAZESTYLE");

        if (strcmp(mazeStyle, "bombed") == 0) {
            _instance = new BombedMazeFactory;

        } else if (strcmp(mazeStyle, "enchanted") == 0) {
            _instance = new EnchantedMazeFactory;

        // ... other possible subclasses

        } else {    // default
            _instance = new MazeFactory;
        }
    }
    return _instance;
}

Note that Instance must be modified whenever you define a new subclass — a problem the registry approach addresses.

Many patterns can be implemented using the Singleton pattern, including Abstract Factory, Builder, and Prototype.

Discussion: choosing among the creational patterns

There are two common ways to parameterize a system by the classes of objects it creates:

  1. Subclass the class that creates the objects — this is Factory Method. Its main drawback is that it can require a new subclass just to change the class of the product, and such changes can cascade (when the product creator is itself created by a factory method, you must override its creator too).
  2. Define an object responsible for knowing the class of the products and make it a parameter of the system — this relies on object composition and is the key aspect of Abstract Factory, Builder, and Prototype. All three involve a new "factory object" that creates products: Abstract Factory produces objects of several classes; Builder builds a complex product incrementally using a correspondingly complex protocol; Prototype builds a product by copying a prototype (here the factory object and prototype are the same object).

Consider the drawing editor's GraphicTool. With Factory Method, a subclass of GraphicTool is created per Graphic subclass. With Abstract Factory, there is a class hierarchy of GraphicsFactories, one per Graphic subclass, each creating just one product. With Prototype, each Graphics subclass implements Clone, and a GraphicTool is parameterized with a prototype.

Which is best depends on many factors:

  • Factory Method is easiest to use at first — easy to define a new GraphicTool subclass, instances created only when the palette is defined — but its subclasses proliferate, and none does very much.
  • Abstract Factory offers little improvement here because it requires an equally large factory hierarchy; it is preferable only if a GraphicsFactory hierarchy already exists.
  • Prototype is probably best for this framework: it requires only a Clone operation per Graphics class, reducing the number of classes, and Clone can serve other purposes (such as a Duplicate menu operation).

Factory Method makes a design more customizable and only a little more complicated, because it requires only a new operation rather than new classes; but it is unnecessary when the instantiated class never changes. Designs using Abstract Factory, Prototype, or Builder are more flexible than Factory Method, but also more complex. Often designs start with Factory Method and evolve toward the other creational patterns as the designer discovers where more flexibility is needed.

Continue exploring

Tags