Singleton Pattern
Design Patterns
Programming
Software Development
Code Best Practices

How to write a Singleton in proper manner?

Object-Oriented Design practice on Codemia

Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.

Practice OOD

Introduction

The hardest part of writing a singleton properly is not the syntax. It is deciding whether a singleton is actually the right design at all. Many singleton implementations fail because they ignore thread safety, lifetime control, testing concerns, or accidental global state. If you truly need one shared instance, the proper implementation should be simple, explicit, and safe under concurrency.

What A Singleton Is Supposed To Guarantee

A singleton usually promises two things:

  • exactly one instance of a type for a given process or scope
  • one shared access path to that instance

That sounds straightforward, but it raises practical questions:

  • when is the instance created
  • who owns its lifetime
  • is it thread-safe
  • how will tests replace or isolate it

If those answers are muddy, the singleton will become a source of bugs.

A Good Java Singleton: Initialization-On-Demand Holder

A clean Java implementation uses the holder pattern.

java
1public final class ConfigurationService {
2    private ConfigurationService() {
3    }
4
5    private static class Holder {
6        private static final ConfigurationService INSTANCE = new ConfigurationService();
7    }
8
9    public static ConfigurationService getInstance() {
10        return Holder.INSTANCE;
11    }
12}

Why this is good:

  • lazy initialization
  • thread-safe because class initialization is handled safely by the JVM
  • no explicit synchronization overhead on normal access

For many Java codebases, this is a much better default than double-checked locking.

Another Strong Java Option: Enum Singleton

If you are in Java and want the most robust built-in singleton semantics, an enum is often the strongest answer.

java
1public enum AppRegistry {
2    INSTANCE;
3
4    public void initialize() {
5        System.out.println("Initialized");
6    }
7}

Usage:

java
AppRegistry.INSTANCE.initialize();

This protects well against serialization and reflection edge cases that can break weaker singleton designs.

Why Many Singleton Implementations Are Bad

A lot of "singleton" code is just a global variable wrapped in a class.

Typical problems:

  • not thread-safe
  • complicated locking logic
  • hidden dependencies across the application
  • hard-to-test code because everything reaches into global state

For example, this lazy singleton is not safe in multi-threaded use:

java
1public final class BadSingleton {
2    private static BadSingleton instance;
3
4    private BadSingleton() {
5    }
6
7    public static BadSingleton getInstance() {
8        if (instance == null) {
9            instance = new BadSingleton();
10        }
11        return instance;
12    }
13}

Two threads could observe instance == null at the same time and create two objects.

Ask Whether You Need A Singleton At All

A proper article on singletons should say this clearly: many singletons should really be dependencies managed by dependency injection.

If your app already has a DI container, a process-wide singleton lifetime can often be declared there without hand-writing a singleton pattern at all.

That gives you:

  • clearer ownership
  • easier tests
  • less hidden global state
  • simpler replacement in different environments

So the best singleton implementation may be: do not implement the pattern manually.

Use Singletons For The Right Things

Singletons fit best when the object truly represents one process-wide service or resource policy, such as:

  • configuration snapshot
  • metrics registry
  • application-wide cache policy

They are a poor fit for ordinary business services that only became global because it was convenient.

Testing Concerns

A hidden cost of singletons is test contamination. Shared mutable state can leak between tests.

If your singleton stores mutable data, tests become harder to isolate. That is one reason immutable or mostly immutable singleton services are safer than stateful ones.

Common Pitfalls

  • Writing a lazy singleton that is not thread-safe.
  • Using a singleton as a shortcut for dependency injection.
  • Storing mutable global state that leaks across tests and requests.
  • Overengineering the implementation with complex locking when the language already offers safer patterns.
  • Assuming "only one instance" automatically means "good design."

Summary

  • A proper singleton must be thread-safe, explicit, and simple.
  • In Java, the holder pattern and enum singleton are both strong options.
  • Many manual singleton implementations are really just unsafe global state.
  • Prefer dependency injection when the application already has a container.
  • Use a singleton only when one shared instance is genuinely part of the design, not just a shortcut.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Object-Oriented Design practice on Codemia

Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.

Practice OOD

All Rights Reserved.