Skip to main content
Design Patterns – Complete Beginner to Advanced Guide
CHAPTER 06 Intermediate

Singleton Pattern

Updated: May 16, 2026
25 min read

# CHAPTER 6

Singleton Pattern

1. Introduction

Imagine you are building a web application that connects to a database. If 10 different classes need to query the database, and every class calls new DatabaseConnection(), you have just opened 10 separate network connections to your database server. If 1,000 users do this, you open 10,000 connections, and your database crashes instantly. You need a way to ensure that *exactly one* connection object is created, and every class in your application shares that exact same instance. This is the exact problem solved by the Singleton Pattern. In this chapter, we will master the Singleton. We will learn how to lock down object creation, implement Lazy Initialization, and explore the dangerous architectural trade-offs of global state.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Define the intent and structure of the Singleton Pattern.
  • Implement a basic Singleton using a private constructor and a static method.
  • Understand the concept of "Lazy Initialization."
  • Analyze the thread-safety issues of Singletons in multi-threaded environments.
  • Discuss why the Singleton is often considered an "Anti-Pattern" in modern testing.

3. The Singleton Concept

The Singleton pattern has two strict requirements:
  1. 1. Ensure a class has only one instance. (Stop people from using new).
  1. 2. Provide a global point of access to that instance. (Let anyone use it easily).

4. Implementation Steps

How do we stop developers from using new?
  1. 1. Make the Constructor Private: If __construct() is private, nobody outside the class can instantiate it.
  1. 2. Create a Static Property: Create a private static variable inside the class to hold the single instance.
  1. 3. Create a Static Getter: Create a public static method (usually called getInstance()). This method checks if the static property is empty. If it is, it creates the object (from *inside* the class) and saves it. If it is not empty, it simply returns the saved object.

5. Lazy Initialization

Notice the logic above: the object is only created *the very first time* someone calls getInstance(). If the application runs but nobody ever needs the database, the connection is never created, saving memory. This is called Lazy Initialization.

6. UML Diagram

*Singleton Class Structure*
text
12345678
---------------------------------
|          Singleton            |
---------------------------------
| - instance: Singleton         |  <-- (Static variable holding itself)
---------------------------------
| - Singleton()                 |  <-- (Private constructor)
| + getInstance(): Singleton    |  <-- (Public static access point)
---------------------------------

7. Code Example (PHP)

Here is the classic implementation of a Database Logger using a Singleton in PHP.
php
12345678910111213141516171819202122232425262728293031323334353637383940
<?php

class Logger {
    // 1. Hold the single instance
    private static $instance = null;

    // 2. Prevent instantiation with 'new'
    private function __construct() {
        echo "Logger initialized.\n";
    }

    // Prevent cloning (a way to bypass 'new')
    private function __clone() {}

    // 3. The global access point
    public static function getInstance() {
        if (self::$instance === null) {
            // Lazy Initialization
            self::$instance = new Logger();
        }
        return self::$instance;
    }

    public function log($message) {
        echo "[LOG]: " . $message . "\n";
    }
}

// Usage:
// $logger = new Logger(); // ERROR: Call to private constructor!

$log1 = Logger::getInstance(); // Outputs: Logger initialized.
$log1->log("First message.");

$log2 = Logger::getInstance(); // (No initialization output, returns existing instance)
$log2->log("Second message.");

// Prove they are the exact same object in memory
var_dump($log1 === $log2); // bool(true)
?>

8. Best Practices (Thread Safety)

If you are writing in a multi-threaded language like Java or C#, the basic Singleton is dangerous. If two threads call getInstance() at the exact same millisecond, they might both see instance == null, and both will create a new object, violating the Singleton rule. *Best Practice:* You must use "Locks" (e.g., synchronized in Java) inside the getInstance() method to ensure only one thread can create the object at a time.

9. Common Mistakes (The Anti-Pattern Debate)

Many senior architects consider the Singleton an Anti-Pattern. Why?
  • Global State: Singletons are essentially glorified global variables. They hide dependencies. If a Checkout class secretly calls Logger::getInstance() deep inside its code, you can't easily see that dependency from the outside.
  • Testing Nightmare: Unit testing a Singleton is incredibly difficult. Because the state is global, if Test A modifies the database Singleton, Test B might fail because it is using the exact same mutated instance.
  • *The Modern Alternative:* Dependency Injection (DI) containers. Modern frameworks (Laravel, Spring) create a single object and *inject* it where needed, achieving the same goal without the global state issues.

10. Mini Project: Build a Configuration Manager

  1. 1. Create a ConfigManager Singleton class.
  1. 2. In its private constructor, load an array of fake settings (e.g., ['theme' => 'dark', 'lang' => 'en']).
  1. 3. Add a getSetting($key) method.
  1. 4. From three different random functions in your script, call ConfigManager::getInstance()->getSetting('theme') to prove they all share the same configuration memory.

11. Practice Exercises

  1. 1. Explain exactly how making a constructor private physically enforces the first rule of the Singleton pattern.
  1. 2. Define "Lazy Initialization." Why is it highly beneficial for heavy objects like Database Connections?

12. MCQs with Answers

Question 1

In the standard Singleton design pattern implementation, how is the global access method (usually named getInstance()) defined?

Question 2

Why is the Singleton pattern frequently criticized as an "Anti-Pattern" by modern software architects who focus heavily on Unit Testing?

13. Interview Questions

  • Q: Write a thread-safe Singleton pattern in your preferred programming language on the whiteboard. Explain how you prevent a race condition during initialization.
  • Q: A junior developer uses the clone keyword to duplicate your Singleton object, successfully bypassing the private constructor. How do you defensively program your Singleton class to prevent cloning?
  • Q: Compare the Singleton pattern to simply defining a massive global variable file. What structural advantages does the Singleton offer?

14. FAQs

Q: Can I pass arguments to getInstance($config)? A: You can, but it is a bad idea. If Thread A calls getInstance('mysql') and creates the object, and later Thread B calls getInstance('postgres'), Thread B will just receive the already-created MySQL instance. This causes massive logical bugs. Singletons should generally be parameterless.

15. Summary

In Chapter 6, we implemented the simplest, yet most highly debated, design pattern: The Singleton. We learned the mechanics of locking down object creation using private constructors and static variables, ensuring strict architectural control over our memory. We utilized Lazy Initialization to optimize performance, and explored the dangers of thread safety in concurrent environments. Most importantly, we addressed the controversy, recognizing that while Singletons solve the problem of shared resources, their hidden global state can wreak havoc on testing and decoupling.

16. Next Chapter Recommendation

The Singleton creates exactly one object. But what if we need to dynamically create many different objects based on user input? Proceed to Chapter 7: Factory Method Pattern.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·