Singleton Implementation, Creational Pattern Comparison, and Adapter Pattern
This document covers three distinct topics from the Gang of Four design patterns book: the implementation details and sample code for the Singleton pattern, a comparative discussion of all creational patterns and when to choose each, and the full treatment of the Adapter structural pattern including its class and object variants.
Key Concepts
Singleton Implementation
The sole instance is accessed through a static Instance() method that uses lazy initialization
The constructor is declared protected to prevent direct instantiation — compile-time enforcement
The _instance static pointer is initialized to 0; created on first access
Global/static object approach is inferior: can't guarantee uniqueness, can't control instantiation order across translation units, can't defer initialization, forces creation of unused singletons
Subclassing singletons is the hard part — the challenge is installing the right subclass instance, not defining the subclass
Three subclass selection strategies: conditional logic in Instance(), link-time selection (put Instance() in the subclass), or a registry of singletons (name-to-instance map)
Registry approach: subclasses register themselves by name; Instance() looks up via environment variable — decouples Instance() from knowing all possible subclasses
Registry bootstrap problem: constructors register instances, but constructors don't run unless someone instantiates — solved with static instances (static MySingleton theSingleton;)
Discussion of Creational Patterns
Two fundamental ways to parameterize a system by product class: subclassing the creator (Factory Method) or object composition (Abstract Factory, Builder, Prototype)
Factory Method: easiest to start with, requires only a new operation, but subclasses proliferate
Abstract Factory: requires a parallel factory class hierarchy — only worthwhile if that hierarchy already exists
Builder: builds complex products incrementally via a complex protocol
Prototype: requires only implementing Clone() on each product — often the best trade-off, reduces class count
Evolutionary trajectory: designs typically start with Factory Method and evolve toward Abstract Factory/Prototype/Builder as flexibility needs emerge
Adapter Pattern
Intent: Convert one interface to another so incompatible classes can work together
Also known as Wrapper
Two forms: class adapter (multiple inheritance) and object adapter (composition)
Pluggable adapters: classes with built-in interface adaptation — minimize assumptions other classes must make, increasing reusability
Two-way adapters: adapt in both directions so either system can use the adapted object transparently; viable when adapted interfaces are substantially different
Prototype vs Factory Method: Prototype reduces class count (no parallel creator hierarchy); Factory Method is simpler but causes subclass proliferation
Adapter vs Bridge: Both involve indirection to another object, but Bridge separates abstraction from implementation by design; Adapter retrofits compatibility onto existing classes
Adapter relates to: Decorator (wraps with same interface to add behavior), Proxy (wraps with same interface for access control), Facade (wraps a subsystem with a simplified interface)
Singleton constructor must be protected (not private) to allow subclassing
Lazy initialization means the instance is created on first access, not at program startup
Three reasons global/static objects fail as Singletons in C++: (1) can't guarantee uniqueness, (2) may lack initialization info at static init time, (3) undefined construction order across translation units
The registry pattern for Singleton subclass selection decouples Instance() from knowing all subclass types
Registry bootstrap uses static instances to trigger constructor-based self-registration