Design Patterns «Prev Next»

Lesson 5 Singleton Design Pattern Structure
ObjectiveStudy the structure of the Singleton.

Singleton Pattern Structure: Parts, Roles, Variants

The goal of this lesson is to study the structure of the Singleton pattern—what parts it contains, how those parts interact, and why the relationships matter more than the names you choose. Singleton is often described as “simple,” but correct implementations must account for modern realities: concurrency, lifecycle boundaries (process vs. distributed), serialization, reflection, and testability.

As you read, keep one key idea in mind: Singleton is not “a magic keyword.” It is a small set of constraints: restricted construction plus a controlled access path that returns the same instance each time.

Core structural parts of Singleton

A canonical Singleton implementation has three structural components. Languages differ, but the roles remain stable.

  1. Instance storage
    • A field that holds the unique instance. In many OO languages this is a static field (class-level), because the instance must be associated with the class, not with individual objects.
    • The name varies (instance, theInstance, INSTANCE), but the role is constant: it is the single reference that all callers share.
  2. Construction restriction
    • A private constructor is the most common mechanism, because it prevents external code from calling new (or equivalent) directly.
    • Some languages may use package-private or protected constructors for frameworks, but that choice should be intentional and documented.
  3. Access method
    • A method (commonly getInstance()) that returns the unique instance and is the only supported access path.
    • This method may also handle initialization (lazy creation) and thread safety (safe publication under concurrency).

Modern boundary reminder: “single instance” almost always means single instance per process (or per application container). In distributed systems, each process can have its own Singleton, so it does not enforce “only one” across a fleet. If you need a single coordinator across nodes, you need distributed coordination (locks/leader election), not an in-process Singleton.

Structural diagram: one class, one controlling relationship

The diagram below captures the essential structure: one class plays the Singleton role, owns a single instance reference, restricts construction, and exposes an access method.

This diagram shows a Singleton class with a static instance field and a getInstance method controlling access to the unique instance.
Singleton structure: one class, one unique instance, one access method.
  1. The pattern consists of a single class playing the Singleton role. The class name is not important; its responsibilities are.
  2. The class maintains a single class-level field (often called theInstance or instance) that references the unique object.
  3. The class exposes an access method (commonly getInstance()) that returns the same instance for all callers.
  4. The constructor is restricted (typically private) so clients cannot create additional instances.

Thread safety and initialization options

Many older Singleton examples focus only on “one instance” and ignore concurrency. In modern code, you should always be explicit about how the instance is created and published safely.

Eager initialization

  • Structure: the instance is created at class load time.
  • Benefits: simple, thread-safe by construction (in many runtimes), predictable.
  • Cost: instance is created even if never used; may do work too early.

Lazy initialization

  • Structure: the instance is created on first call to the access method.
  • Benefits: avoids startup work until needed.
  • Risk: must be implemented carefully under concurrency.

Recommended structural variants (common in modern Java)

The following implementations preserve the same structural roles (instance storage + restricted construction + access method), but improve correctness and maintainability:

  1. Initialization-on-demand holder idiom (lazy, thread-safe, minimal overhead)
    public final class SingletonService {
    
        private SingletonService() { }
    
        private static class Holder {
            private static final SingletonService INSTANCE = new SingletonService();
        }
    
        public static SingletonService getInstance() {
            return Holder.INSTANCE;
        }
    }
  2. Enum-based Singleton (strong choice when language supports it; robust vs. serialization)
    public enum AppRegistry {
        INSTANCE;
    
        public void register(String key, Object value) {
            // ...
        }
    }

    This form is frequently recommended because it is naturally single-instance, resists common serialization pitfalls, and is straightforward. It also makes the “Singleton-ness” explicit in the type system.

  3. Double-checked locking (works in modern Java with volatile, but more complex than needed)
    public final class Singleton {
    
        private static volatile Singleton instance;
    
        private Singleton() { }
    
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    This is a valid structural variant in modern Java, but it is often taught more than it is used. Prefer simpler patterns unless you have a specific reason to choose this one.

Why the “static flag in constructor” approach is legacy

Some legacy examples attempt to detect multiple constructions by setting a static flag in the constructor and throwing an exception if construction happens twice. This illustrates the idea, but it is not the best structural enforcement mechanism. The stronger rule is: prevent external construction in the first place (private constructor), then provide one controlled access method.

// Legacy illustration (avoid in production): a static flag checked during construction
static boolean instance_flag = false;

Throwing a custom exception can still be useful pedagogically (it demonstrates enforcement), and it can help detect reflection misuse in some cases. However, treat it as a supplement, not the primary enforcement mechanism.

class SingletonException extends RuntimeException {
  public SingletonException() { super(); }
  public SingletonException(String s) { super(s); }
}

Structure beyond code: lifecycle, scope, and testability

In modern systems, “structure” includes more than fields and methods. You also need a clear answer to these questions:

  1. Scope: Is it one instance per classloader, per process, per container, or per request? (Singleton typically implies per-process/per-container.)
  2. Lifecycle: When does it start, and does it need explicit shutdown (threads, file handles, sockets)?
  3. State: Is state immutable, or will concurrent callers mutate it? If mutable, what is the synchronization strategy?
  4. Testing: Can consumers be tested without hidden global state? If not, consider dependency injection or passing an interface explicitly.

A common modern pattern is: keep a single instance, but let your application assembly (composition root / DI container) own the lifecycle and injection. That preserves “one instance” while avoiding hardwired global access in every consumer.


The names in the diagram and code are not important. What matters is the relationship between parts: a class-level instance reference, restricted construction, and a controlled access method. Also note that structural diagrams typically show only the pattern-relevant parts of a class. A real Singleton class will almost always include additional domain methods (for example, play() on an audio controller), but those methods are outside the pattern’s defining structure.


Singleton Pattern - Quiz

Click the link below to take a short quiz on the material that was just covered.
Singleton Pattern - Quiz

SEMrush Software 5 SEMrush Banner 5