Introduction
Singleton and Factory Method are two of the most widely used Creational design patterns. Singleton ensures a class has only one instance and provides a global point of access to it. Factory Method defines an interface (or function) for creating an object but lets subclasses, or a parameterized creator function, decide which concrete class to instantiate. Both patterns exist to control 'how' and 'when' objects come into being, rather than leaving instantiation scattered throughout the codebase.
Cricket analogy: Singleton is like a team having exactly one official scorer whose scorecard is the single source of truth for the match, while Factory Method is like a selector function that decides which type of bowler to bring on, pacer, spinner, or all-rounder, based on the situation, without the captain hardcoding the choice.
Explanation
A Singleton is useful when exactly one object is needed to coordinate actions across a system — for example, a configuration manager, a logging service, or a connection pool. It typically works by making the constructor private (or restricted) and exposing a static/class-level accessor that creates the instance on first use and returns the cached instance thereafter. Overuse of Singleton is a common anti-pattern because it introduces global state, which makes unit testing harder and can hide dependencies.
Cricket analogy: A Singleton is useful for a single official scoreboard that coordinates runs and wickets across the ground; it works by restricting who can update it directly and exposing one official channel that creates the record on first ball and returns the same running total thereafter, though over-relying on one global scoreboard for local team stats makes independent analysis harder.
Factory Method, by contrast, addresses a different problem: a class cannot anticipate which concrete class of object it needs to create. Instead of littering the codebase with direct calls to concrete constructors (e.g., PDFExporter(), CSVExporter()), client code calls a factory with a parameter (e.g., create_exporter("csv")), and the factory encapsulates the decision of which concrete class to instantiate. This keeps object-creation logic in one place and makes it easy to add new types without modifying client code — an application of the Open/Closed Principle.
Cricket analogy: Factory Method addresses picking the right bowler for a situation: instead of the captain hardcoding 'bring on Bumrah' every time, they call a selector with a parameter like 'need a wicket' or 'need to save runs,' and the selector encapsulates which bowler type, pace or spin, actually gets the ball.
Example
import threading
# --- Singleton: thread-safe lazy instantiation ---
class ConfigManager:
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.settings = {}
return cls._instance
def set(self, key, value):
self.settings[key] = value
# --- Factory Method: delegate creation based on a parameter ---
class Exporter:
def export(self, data):
raise NotImplementedError
class CSVExporter(Exporter):
def export(self, data):
return ",".join(data)
class JSONExporter(Exporter):
def export(self, data):
import json
return json.dumps(data)
def create_exporter(kind: str) -> Exporter:
exporters = {"csv": CSVExporter, "json": JSONExporter}
if kind not in exporters:
raise ValueError(f"Unknown exporter: {kind}")
return exporters[kind]()
if __name__ == "__main__":
a = ConfigManager()
b = ConfigManager()
a.set("debug", True)
print(a is b, b.settings) # True {'debug': True}
exporter = create_exporter("json")
print(exporter.export(["x", "y", "z"]))Analysis
In the Singleton example, __new__ is overridden so that only one instance is ever created; the lock guards against a race condition where two threads might otherwise both see _instance is None at the same time. In the Factory Method example, create_exporter is the single place that knows how to map a string to a concrete Exporter subclass — client code never calls CSVExporter() or JSONExporter() directly. Adding a new format, such as XML, means adding one class and one dictionary entry, without touching any code that calls create_exporter.
Cricket analogy: In the scoreboard Singleton, only one official record is ever created, guarded so two officials updating it at the exact same ball don't create conflicting totals; in the bowler-selector Factory, select_bowler is the single place mapping situation to bowler type, so adding a new bowling style, like a mystery spinner, means adding one class without touching the captain's calling code.
A common mistake is conflating Singleton with Factory: Singleton constrains 'how many' instances exist, while Factory Method constrains 'which class' gets instantiated. They can be combined (a Factory that itself is a Singleton) but they solve orthogonal problems. Another common mistake is implementing Singleton without thread safety, which in multi-threaded environments can accidentally create two 'singleton' instances.
Cricket analogy: Confusing Singleton with Factory is like confusing 'there is only one official scorer' with 'the selector decides pace or spin'; a scoring system could combine both, one Singleton scorer that internally uses a Factory to instantiate the right delivery-type record, but they solve different problems, and a scorer implemented without safeguards against simultaneous updates from two officials can produce two conflicting 'official' totals.
Key Takeaways
- Singleton guarantees exactly one instance of a class and a global access point to it.
- Factory Method centralizes the decision of which concrete class to instantiate based on input.
- Singleton controls instance count; Factory Method controls instance type — they are independent concerns.
- Overusing Singleton introduces hidden global state that complicates testing.
- Factory Method supports the Open/Closed Principle by letting new types be added without modifying client code.
Practice what you learned
1. What is the primary purpose of the Singleton pattern?
2. In Factory Method, who decides which concrete class is instantiated?
3. What is a common criticism of overusing the Singleton pattern?
4. Why does adding a new exporter type in the Factory Method example not require changing existing client code?
Was this page helpful?
You May Also Like
Design Patterns Overview
An introduction to the Gang of Four design patterns and their three categories: Creational, Structural, and Behavioral.
Builder and Prototype Patterns
Understand how Builder constructs complex objects step by step and how Prototype creates new objects by cloning existing ones.
Single Responsibility and Open/Closed Principles
Learn to split classes by responsibility and extend behavior with polymorphism instead of editing existing code.
SOLID Principles Overview
A guided tour of the five SOLID design principles that make object-oriented code easier to maintain and extend.
Related Reading
Related Study Notes in Software Engineering
Browse all study notesMicroservices Study Notes
Software Architecture · 30 topics
Software EngineeringTesting & TDD Study Notes
Software Testing · 30 topics
Software EngineeringDesign Patterns Study Notes
Software Design · 30 topics
Software EngineeringGit & Version Control Study Notes
Bash · 40 topics
Software EngineeringSystem Design Study Notes
Architecture · 40 topics