Singleton
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
π StructureDashed line animations indicate the flow direction of data or requests
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.
Singleton is one of the simplest yet most debated of the 23 design patterns catalogued by the Gang of Four (GoF) in 1994. In early object-oriented design it was welcomed as a clean way to solve global resource access. But as projects scaled, criticism emerged that Singleton creates coupling similar to global variables and is difficult to replace with mocks in tests. Modern frameworks increasingly use DI (Dependency Injection) containers to manage instance lifecycles, effectively replacing Singleton's role. Even so, the pattern remains worth understanding because it still appears frequently in legacy code and the same concept lives on inside DI containers under the name 'singleton scope.'
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.
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.
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.