skip to content

What problem does the Singleton design pattern solve, and how is it different from simply declaring a global variable?

level: juniorimportance: must knowfreq 82%

answer

  1. two promises: uniqueness + global access point
  2. private constructor is the enforcement
  3. global var shares but doesn't forbid a second
  4. accessor = method → can lazy-create, add logic
  5. DI singleton scope = uniqueness without the global

basics

~20 s

Singleton ensures a class has exactly one instance and one shared way to reach it. Unlike a plain global variable, the class hides its own constructor and creates the instance itself, so no other code can make a second one.

solid answer

~50 s

Singleton is a creational pattern with two intents bundled together: (1) guarantee that at most one instance of a type exists for a given scope, and (2) provide a well-known global access point to it. The classic implementation makes the constructor private/inaccessible and exposes a static accessor that returns the one instance, creating it if needed. A global variable only does the second half — it publishes a reference but does not prevent anyone from constructing more instances, and it does not control when construction happens. Singleton adds enforced uniqueness plus lifecycle control (lazy creation, initialization logic). The price is that both intents are baked into the class itself: callers reach it statically, so the dependency is invisible in signatures. In modern code the uniqueness half is usually delegated to a DI container's singleton scope, which keeps one instance without the global access point.

code

pseudocode · 13 lines
pseudocode
// Global variable: shares, but does not enforce
global registry = new Registry()      // anyone may also do `new Registry()`

// Singleton: shares AND enforces
class Registry {
    private static Registry instance
    private Registry() { /* setup */ }        // constructor is unreachable outside

    static Registry getInstance() {
        if (instance == null) instance = new Registry()   // lazy creation
        return instance
    }
}

go deeper

for a junior

State the two promises (one instance, one access point) and name the mechanism: private constructor plus a static accessor. Contrast with a global variable, which can't stop a second instance.

for a middle

Add creation control — lazy vs eager, the accessor can run setup logic — and give a concrete legitimate use (exclusive external resource, in-process registry).

for a senior

Separate the two intents explicitly and argue that uniqueness is usually worth keeping while global access is usually worth dropping in favour of injection.

for a principal

Frame it as a coupling decision: the static accessor is an architectural back-channel that lets any module depend on any other invisibly. Discuss where you would still allow it (bootstrap, logging facade) and how you'd fence it off.

## The two promises The Singleton pattern (one of the Gang of Four *creational* patterns — patterns about **how objects get created**) makes two separate promises: 1. **Uniqueness** — for some scope (usually one running process), there is *at most one* instance of the type. 2. **Global access** — any code, anywhere, can obtain that instance without being handed it, typically via a static/class-level accessor such as `Config.getInstance()`. It is worth internalising that these are *independent* ideas. You can have uniqueness without global access (a container creates one object and injects it everywhere). You can have global access without uniqueness (a public mutable global that anyone can reassign). Singleton fuses them, and most of the pattern's reputation — good and bad — comes from that fusion. ## Canonical shape ``` class Registry: private static instance = null # class-level storage private constructor() # nobody outside can call `new` static getInstance(): if instance == null: instance = new Registry() # the class creates itself return instance ``` Three mechanical ingredients: - **Inaccessible constructor** (private/internal). This is what enforces uniqueness — without it the pattern is just a convention. - **Class-level (static) storage** for the single instance, so it survives independently of any caller. - **A static accessor** that hands out that instance and, in the lazy variant, creates it on first use. ## Why a plain global variable is not the same thing | | Global variable | Singleton | |---|---|---| | Prevents a second instance | No — anyone can `new` the type again | Yes — constructor is inaccessible | | Controls *when* the object is built | No — built at declaration/startup | Yes — can defer to first use, run setup logic, pick a subclass | | Can add behaviour on access | No | Yes — the accessor is a method (logging, validation, lazy config read) | | Publishes a well-known name | Yes | Yes | A global gives you *sharing*. Singleton gives you *sharing plus enforcement plus creation control*. That extra enforcement is the only technical justification for the pattern; if you don't actually need to forbid a second instance, you don't need Singleton. ## When enforced uniqueness genuinely matters - The object wraps a **truly exclusive external resource** — a single hardware device handle, a process-wide lock file, one native library that must be initialised once. - The object's whole purpose is **coordination**: an in-process registry, a metrics collector, an ID generator whose contract is broken if two copies exist and each hands out overlapping IDs. - **Cost/identity semantics**: a cache whose value depends on everyone sharing the *same* map; two caches would silently be a correctness bug, not just waste. If the reason is merely *"it's expensive to create"* or *"we only ever need one"*, that is a **lifecycle/configuration** decision, and the right tool is one instance created at composition time and passed around — not a class that forbids alternatives. ## Stateless singletons are a much weaker claim If the type holds no mutable state (a pure formatter, a validator), "one instance" is a memory micro-optimisation, and most of the classic criticisms evaporate. But so does most of the justification: a stateless object can be freely constructed, so you gain nothing from forbidding it. The pattern's teeth — and its risks — come from **shared mutable state**. ## Related, often-confused patterns - **Monostate / Borg**: many instances, all sharing the same static state. Same coupling problems, different surface. - **Static utility class**: no instance at all, only static functions. Cannot implement an interface or be substituted; not a Singleton, though people call it one. - **DI singleton scope**: the container creates one instance and injects it. Uniqueness *within the container*, no global accessor, trivially replaceable in tests. This is what most modern frameworks mean when they say "singleton". ## The honest summary for an interview Singleton is easy to implement and easy to over-apply. Say what it guarantees (one instance, one access point), how it guarantees it (hidden constructor + static accessor), and immediately note that the *access point* half is what people regret, because it turns an explicit dependency into an invisible one.

  • If you only ever need one instance, why not always use Singleton?
    Because "we need one" is a lifecycle decision, while Singleton is a *design* decision that also forbids alternatives and hides the dependency. Creating one instance at startup and injecting it gives the same single instance while keeping the dependency explicit and replaceable in tests.
  • Is a class with only static methods a Singleton?
    No. It is a static utility class — there is no instance at all. It cannot implement an interface, be passed as a parameter, or be substituted at runtime, so it is even less flexible than a Singleton, though it shares the same global-access coupling.

A country has exactly one official time service. A global variable is a clock hanging in the town square — everyone can read it, but nothing stops a neighbouring town from hanging up a second, differently-set clock. Singleton is the law that says only one authority may exist and everyone must call it — which is powerful precisely because it forbids the second clock, and annoying because now every appointment implicitly depends on that one office being open.

saying these in an interview costs you the question

  • "Singleton just means a global variable" — it also enforces that no second instance can be constructed.
  • "Singleton means one instance in the whole system" — it is one per scope, typically one per process/classloader, not one per cluster.
  • Claiming the main benefit is memory savings; sharing one object is rarely the point, coordination of shared state is.
  • Forgetting to make the constructor inaccessible, which turns the whole thing into a convention nobody is forced to follow.
  • Confusing the pattern with a DI container's singleton *scope*, which deliberately omits the global access point.

context