Conceptly
← All Concepts
1️⃣

Singleton

CreationalA creational pattern that ensures only one instance exists per process

Singleton is a creational pattern that guarantees exactly one instance of a class exists within a process and provides a global access point to that instance. Because the constructor is hidden and the object is only exposed through a static method, the same instance is returned regardless of who calls it or when.

β–ΆArchitecture Diagram

πŸ” Structure

Dashed line animations indicate the flow direction of data or requests

Why do you need it?

As an application grows, multiple modules inevitably need to share the same resource: a database connection pool, a logger, a configuration object. If each module creates its own, connection counts balloon out of control or configuration values diverge across modules. Using a global variable makes access easy, but the initialization timing is unclear and anyone can overwrite the value, making state changes hard to trace. Without a way to enforce 'this object must be exactly one' at the code level, you are left relying on convention, and convention breaks the moment someone forgets.

Why did this approach emerge?

In early object-oriented systems, global resources were often handled through raw global variables or ad hoc static state. That held up while applications were small, but as modules multiplied it became hard to track who created the object, when initialization happened, and why multiple copies of the same role were appearing. The pressure was to enforce 'this resource must exist only once per process' through structure rather than convention. Singleton emerged as a way to hide construction and concentrate access through one controlled entry point.

How should you read it today?

Today, Singleton still matters as a foundational pattern for understanding why some resources become lifecycle-managed shared instances, but it is no longer read as the default recommendation for application design. Modern frameworks use DI containers and scopes to achieve the same effect more explicitly and make substitution in tests easier. So the most accurate modern reading is not 'always implement this directly,' but 'understand why lifecycle control and dependency visibility matter.'

How does it work inside?

Singleton's mechanism breaks into three steps. First, the constructor is made private. No one outside the class can call new, so a second instance cannot be created. Second, the class holds a static field for its own instance. Initially it is empty. Third, a static method such as getInstance() provides access. If the static field is empty, it creates one instance and stores it; if the field is already populated, it returns the existing one. No matter how many times the method is called, the returned object is always the same. In multithreaded environments, two threads calling getInstance() simultaneously could create two instances, so synchronization mechanisms such as a synchronized block or a volatile field must be added.

Boundaries & Distinctions

Singleton and a global variable both create 'a single value accessible from anywhere.' But a global variable delegates initialization timing and lifecycle to the language runtime and can be overwritten by anyone, whereas Singleton controls when creation happens and prevents external code from making a new instance. Singleton and a DI container's singleton scope produce the same result of 'one instance,' but they differ in who manages the lifecycle. The Singleton pattern has the class itself enforce 'I am the only one,' while a DI container achieves the same effect through external configuration. When you need to swap out instances for testing or make dependencies explicit, DI is more flexible. Conversely, when you need to lock down a global resource to one object without a framework, Singleton is the most straightforward approach.

Trade-off

The upside is clear. Singleton enforces one shared instance for resources that truly must not diverge, reducing duplicate creation and inconsistent state while giving callers a simple access path. The cost is equally real. A global access point creates hidden dependencies, makes tests harder to isolate, and turns initialization order or leaked state into system-wide problems. The decision rule is: does this resource genuinely need to be singular, or is sharing merely convenient? If it truly must be one, Singleton or a container-managed singleton scope is a candidate. If sharing is only convenience, explicit injection is usually safer.

When should you use it?

Singleton appears whenever a resource must exist exactly once within a process. Loggers, configuration managers, connection pools, and thread pools are the classic examples. Creating multiples of these wastes resources or causes state to drift, so they must be fixed to one and made accessible everywhere. However, overusing Singleton increases hidden dependencies between classes and makes state difficult to reset in tests. If you start making business logic objects into Singletons, the code becomes increasingly tied to global state. Before applying it, it is worth asking: 'Does this object truly need to be one, or is it just convenient to share the same instance?' If the answer is the latter, lifecycle management through a DI container may be more appropriate.

Configuration management -- sharing a single config object across the entire app to prevent inconsistencyLogging -- multiple modules writing to the same logger instanceConnection pool -- maintaining a single database connection pool to prevent resource wasteCache -- managing a global cache store through a single entry point