A certain few of the GOF design patterns show up a lot, in most cases programmers use them without consciously deciding to “use a design pattern”.

  • singleton: most often this is nothing more than a global variable in OOP clothing - a static method to get. People will always need global variables.
  • iterator pattern: used in almost every standard library with containers, and that’s not counting “incremented pointers”, even though that often can provide a similar interface.
  • strategy pattern: often using some form of language-provided dynamic dispatch (virtual, or functor). Picking behavior at run time is a very powerful tool, even if at some level it just boils down to branching, switching, or looking up.
  • Factory pattern: essentially just the strategy pattern applied to object creation, usually achieved in the same ways.
  • observer pattern: nearly every game engine or other system with “tasks” will have something similar. Some forms of reference counting and concurrency even imply its use.
  • visitor pattern: the popularity of observer and strategy pattern often results in many uses of the visitor pattern: “I have a list of objects I need to inform of an event, but the objects are generic. I’ll have them all share some common interface so I can iterate over them and call the right method for each.”

Creational patterns

Abstract factory

Factory method

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

The Factory Method design pattern solves problems like:

  • How can an object be created so that subclasses can redefine which class to instantiate?
  • How can a class defer instantiation to subclasses?

The Factory Method design pattern describes how to solve such problems:

  • Define a separate operation (factory method) for creating an object.
  • Create an object by calling a factory method.

Implementations

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public abstract class Room {
    abstract void connect(Room room);
}

public class MagicRoom extends Room {
    public void connect(Room room) {}
}

public class OrdinaryRoom extends Room {
    public void connect(Room room) {}
}

public abstract class MazeGame {
     private final List<Room> rooms = new ArrayList<>();

     public MazeGame() {
          Room room1 = makeRoom();
          Room room2 = makeRoom();
          room1.connect(room2);
          rooms.add(room1);
          rooms.add(room2);
     }

     abstract protected Room makeRoom();
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class MagicMazeGame extends MazeGame {
    @Override
    protected MagicRoom makeRoom() {
        return new MagicRoom();
    }
}

public class OrdinaryMazeGame extends MazeGame {
    @Override
    protected OrdinaryRoom makeRoom() {
        return new OrdinaryRoom();
    }
}

MazeGame ordinaryGame = new OrdinaryMazeGame();
MazeGame magicGame = new MagicMazeGame();

Singleton

The pattern is useful when exactly one object is needed to coordinate actions across a system.

The singleton pattern allows objects to:

  • Ensure they only have one instance
  • Provide easy access to that instance
  • Control their instantiation (for example, hiding the constructors of a class)

Common uses

Singletons are often preferred to global variables because they do not pollute the global namespace (or their containing namespace). Additionally, they permit lazy allocation and initialization, whereas global variables in many languages will always consume resources.

Logging is a common real-world use case for singletons, because all objects that wish to log messages require a uniform point of access and conceptually write to a single source.

Implementations

Implementations of the singleton pattern ensure that only one instance of the singleton class ever exists and typically provide global access to that instance.

Typically, this is accomplished by:

  • Declaring all constructors of the class to be private, which prevents it from being instantiated by other objects
  • Providing a static method that returns a reference to the instance

The instance is usually stored as a private static variable; the instance is created when the variable is initialized, at some point before when the static method is first called.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>

class Singleton {
public:
  // defines an class operation that lets clients access its unique instance.
  static Singleton& get() {
    // may be responsible for creating its own unique instance.
    if (nullptr == instance) instance = new Singleton;
    return *instance;
  }
  Singleton(const Singleton&) = delete; // rule of three
  Singleton& operator=(const Singleton&) = delete;
  static void destruct() {
    delete instance;
    instance = nullptr;
  }
  // existing interface goes here
  int getValue() {
    return value;
  }
  void setValue(int value_) {
    value = value_;
  }
private:
  Singleton() = default; // no public constructor
  ~Singleton() = default; // no public destructor
  static Singleton* instance; // declaration class variable
  int value;
};

Singleton* Singleton::instance = nullptr; // definition class variable

int main() {
  Singleton::get().setValue(42);
  std::cout << "value=" << Singleton::get().getValue() << '\n';
  Singleton::destruct();
}

Builder

Structural patterns

Facade

Proxy

Behavioral patterns

Observer

Strategy

Concurrency patterns