GofPatterns GofPatterns


Singleton Pattern   «Prev  Next»
Lesson 10 Singleton: related patterns
ObjectiveHow other Patterns work with or substitute for Singleton Patterns

How other Patterns work with or substitute for Singleton Patterns?

Patterns do not exist in a vacuum any more than classes or objects do. Most significant object-oriented systems designed with patterns use more than one.
The Singleton pattern prevents objects from being created, specifically objects of its class other than the one unique instance. Most other creational patterns actually create many different objects of some class. You can think of these creational patterns as machines cranking out objects on an assembly line.

However, the analogy only stretches so far. In particular, two machines do not create objects any faster than one machine. Therefore, it is common (though not required) to implement various creational patterns with Singleton patterns.
In particular, the
  1. Abstract Factory,
  2. Builder, and
  3. Prototype patterns
are often implemented with Singleton classes.
In the course project you will also encounter an example of part of a behavioral pattern, Observer, implemented as a Singleton.

Gang of Four Patterns

Basic C++ Idioms Supporting Singletons

Most often, singletons are implemented in C++ by using some variation of the following idiom:
// Header file Singleton.h
class Singleton{
 public:
  // Unique point of access
  static Singleton* Instance(){
   if (!pInstance_)
    pInstance_ = new Singleton;
    return pInstance_;
  }
  // ----- operations -----
 private:
 Singleton(); // Prevent clients from creating a new Singleton
 Singleton(const Singleton&); 
 /* Prevent clients from creating a copy of the Singleton */
 static Singleton* pInstance_; // The one and only instance
};
// Implementation file Singleton.cpp
Singleton* Singleton::pInstance_ = 0;

Because all the constructors are private, user code cannot create Singletons. However, Singleton's own member functions are allowed to create objects. Therefore, the uniqueness of the Singleton object is enforced at compile time. This is the essence of implementing theSingleton design pattern in C++.
If it's never used (no call to Instance occurs), the Singleton object is not created. The cost of this optimization is the (usually negligible) test incurred at the beginning of Instance. The advantage of the build-on-first-request solution becomes significant if Singleton is expensive to create and seldom used.
An ill-fated temptation is to simplify things by replacing the pointer pInstance_ in the previous example with a full Singleton object.
// Header file Singleton.h
class Singleton{
 public:
  // Unique point of access
  static Singleton* Instance() {
   return &instance_;
  }
  int DoSomething();
  private:
   static Singleton instance_;
};
// Implementation file Singleton.cpp
Singleton Singleton::instance_;

This is not a good solution. Although instance_ is a static member of Singleton (just as pInstance_ was in the previous example), there is an important difference between the two versions. instance_ is initialized dynamically (by calling Singleton's constructor at runtime), whereas pInstance_ benefits from static initialization (it is a type without a constructor initialized with a compile-time constant).
The compiler performs static initialization before the very first assembly statement of the program gets executed. (Usually, static initializers are right in the file containing the executable program, so loading is initializing.) On the other hand, C++ does not define the order of initialization for dynamically initialized objects found in different translation units, which is a major source of trouble. (A translation unit is, roughly speaking, a com-pilable C++ source file.) Consider this code:

// SomeFile.cpp
#include "Singleton.h"
int global = Singleton::Instance()->DoSomething();

Depending on the order of initialization that the compiler chooses for instance_ and global, the call to Singleton::Instance may return an object that has not been constructed yet. This means that you cannot count on instance_ being initialized if other external objects are using it.