Home Design Patterns Singleton

Singleton

Creational Gang of Four

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

Gamma, Helm, Johnson & Vlissides, p. 127

Purpose

Singleton makes a class responsible for its own uniqueness. The constructor is private so nobody else can allocate one, a static field holds the only instance, and a static accessor hands it out. Ask twice and you get the same object.

Read the intent again, because it contains two separate promises joined by an "and". The first is that exactly one instance exists. The second is that it can be reached from anywhere. Almost every real requirement is for the first one. Almost every problem this pattern causes comes from the second. That the book welds them together in a single sentence is, I think, the most consequential wording decision in the whole catalog.

Design

Three elements: a private constructor, a static field, and a static accessor. The variations are all about when the instance is created and what happens if two threads ask at once. Creating it eagerly, in the static initializer, needs no locking at all and is the right default. Creating it lazily needs care, and the history here is unhappy: the double-checked locking idiom that everyone copied through the nineties was broken in Java until the memory model was revised in Java 5, and was not guaranteed in C++ until C++11 made function-local static initialization thread-safe.

UML class diagram: two client classes both depend on a single Singleton class, which holds a static instance field, a private constructor and a static getInstance method.
Both clients depend on the Singleton and neither one was given a reference to it. That is the part to look at: the dependency exists, and it appears in no constructor, no parameter list and no field.

If you decide you do want one, prefer whatever your language already guarantees over anything you write by hand. In Java that is an enum with a single constant, or a static holder class. In C# a static readonly field initialized inline. In C++ a function-local static. Hand-rolled locking here has a long history of looking right and being wrong on some architecture you do not own.

Example

The Autobot Robotics controller talks to the servo drives over one physical CAN interface. There is precisely one of them, it is a piece of hardware, and two objects independently writing frames to it produce garbage. Uniqueness here is not a design preference, it is a property of the machine, so ServoBus became a singleton and stayed one for years without causing anybody any trouble.

Two things ended that. The offline simulator needed to run two arms in one process, and could not, because the static field means one bus per process rather than one bus per arm. That was survivable; they ran two processes. The test suite was worse. Every test that touched motion shared the same bus state, so tests passed alone and failed in a suite, failures depended on ordering, and the whole suite had to run single-threaded on a build machine with plenty of idle cores. Nobody could substitute a fake bus without adding a static setter, which is a back door that then exists in production code forever.

The fix kept the guarantee and dropped the global. ServoBus is constructed exactly once, in startup, and passed to whatever needs it. There is still one instance per arm. There is no longer any code that can reach it without having been given it, and the tests hand over a fake in one line. The class diagram barely changed; what changed is that the dependency became visible.

class ServoBus                     // the classic form
{
    static readonly ServoBus instance = new ServoBus();

    private ServoBus()
    {
        // claim the CAN interface
    }

    public static ServoBus getInstance() { return instance; }

    public void send(Frame f) { }
}

class MotionController
{
    public void move()
    {
        ServoBus.getInstance().send(new Frame());
        // nothing in this class's signature says it touches hardware
    }
}


class MotionControllerV2            // what replaced it
{
    readonly ServoBus bus;
    public MotionControllerV2(ServoBus b) { bus = b; }

    public void move() { bus.send(new Frame()); }
}

class Startup
{
    public void main()
    {
        ServoBus bus = new ServoBus();   // still exactly one, created here
        MotionControllerV2 c = new MotionControllerV2(bus);
    }
}
The two versions have the same number of instances at run time. The difference is the constructor on MotionControllerV2: the dependency is now written down, which is what makes it substitutable in a test and visible to whoever reads the class next.

Seen in the Wild

The JDK has genuine ones: Runtime.getRuntime() returns the single object representing the running JVM, and Toolkit.getDefaultToolkit() the single AWT toolkit. Python's logging module keeps one logger per name in a process-wide registry, which is a Multiton rather than a Singleton, and Python's None, True and False are singletons enforced by the interpreter. The instructive one is Spring, whose default bean scope is called singleton and means one instance per container: same lifetime guarantee, reached by injection instead of a static call, and that one change is why nobody complains about Spring's singletons the way they complain about the pattern.

Comparisons

  • Multiton is the same mechanism keyed by name: one instance per key rather than one in total. It inherits every problem on this page and adds a cache eviction question.
  • Dependency Injection is the replacement, not an alternative. It gives you the one-instance guarantee without the global access, which is the only half worth having.
  • Flyweight shares objects for memory reasons and is happy to have thousands of them. A Singleton is a flyweight pool with exactly one key.
  • Facade is the class most often turned into a singleton, and the combination is worse than either. A global entry point to an entire subsystem gives every class in the program a dependency on all of it.
  • Lazy Load is the half of the pattern people usually actually want when they write the double-checked locking version: deferred initialization, which has nothing to do with uniqueness.

Why It Exists

My take

The uniqueness half of this pattern answers a real need. Hardware is genuinely singular. There is one CAN controller, one register block, one interrupt vector table, and a language that lets you allocate a second object claiming to own the first one is a language that will let you write a bug you cannot debug. Wanting the compiler to enforce "there is one of these" is entirely reasonable, and no mainstream language gives you a good way to say it.

The global access half exists for a reason too, but the reason has expired. In 1994, in C++, with no containers and no established idea of a composition root, a static accessor was the only portable way to get an object into code you did not write and could not thread a parameter through. The pattern is partly a workaround for the absence of infrastructure that we now have. That infrastructure arrived, and the pattern did not get revised.

So my position is that this is two patterns wearing one name, and the catalog would be healthier if it had separated them. "Exactly one instance" is a constraint worth stating. "Reachable from anywhere" is a global variable, and giving it a class, a private constructor and a static accessor does not change what it is. Every argument for a singleton I have heard in twenty years turned out, on inspection, to be an argument for the first half and a convenience preference for the second.

The one place I will still defend the classic form is deep in embedded code where there is no allocator to speak of, the object is created at link time, and the alternative is threading a pointer through eleven layers of C that was written before anybody involved had heard of dependency injection. That is a real constraint. It is also a much narrower situation than the popularity of this pattern would suggest.

Criticisms

Singleton is widely treated as an anti-pattern and I think that judgment is broadly correct. The core objection is simple: it is a global variable with ceremony. A method that calls ServoBus.getInstance() depends on the servo bus, but its signature says otherwise, so you cannot tell what a function touches by reading it, and you cannot tell what a change will affect without searching the whole codebase for static calls. Every dependency the pattern hides is a dependency that stops being reviewable.

The testing damage is the part that costs teams the most, and it is not subtle. Shared mutable state persists between tests, so results depend on execution order and a suite that passes on your machine fails on the build server. Substituting a fake requires either a static setter, which is a mutable global with a nicer name, or a test framework that rewrites bytecode, which is a large hammer to bring to a problem you created. Tests cannot run in parallel because they contend on the same instance. I have watched a test suite go from eleven minutes to under two purely by removing statics, and none of that time was spent making the tests faster.

Concurrency is hostile territory for it in both directions. Getting the instance safely is the famous part: double-checked locking was broken in Java before Java 5 and unguaranteed in C++ before C++11, and a great deal of published code from that era is wrong. The less discussed part is that once you have the instance, it is a single object that every thread in the process shares, so it becomes the contention point, and the lock it needs to protect itself is a lock the whole system now queues behind. Lifetime is no better: C++ has an entire named hazard, the static destruction order fiasco, for the case where one singleton uses another during teardown, and lazy initialization in a real-time path means allocating at a moment you did not choose.

Underneath all of that, the pattern makes a permanent bet that there will never be two, and that bet is repeatedly lost. Two databases after the read replica arrives. Two loggers when a customer wants one per cell. Two arms in a simulator. The change from one to two is trivial if the object was passed in, and touches every call site if it was reached statically. Constructing it once at startup and handing it to whoever needs it costs one parameter and gives you the same guarantee. That is the whole argument, and after twenty years I have not found a case in application code where it came out the other way.