Singleton
Every module builds its own Config.
Hide the constructor and keep the one instance inside the class.
Exactly one object must coordinate things (config, registry, connection pool)
The picture from life
A school has one principal. Whoever wants the principal asks the office and is shown the same person; nobody hires a second principal just because they have a question. The school makes sure there is only ever one.
The problem
What goes wrong when you do not use it.
Every module builds its own Config. One reads the file, another reads it again, a third changes a value that the others never see. You now have three "truths" and wasted loads.
- creates
- Exactly one object must coordinate things (config, registry, connection pool)
- Everyone must see the same state
- Creating it is expensive and should happen once
The idea
How the pattern fixes it.
Hide the constructor and keep the one instance inside the class. A static get() creates it the first time and hands back the same object afterwards.
- calls / uses
Who is who
- Config
- The class that must have one instance. Constructor is private (or by convention not called).
- get()
- The only door. Creates on first call, returns the saved object afterwards.
- Callers
- Always use Config.get(); never create one.
Build it in steps
- 1Make the constructor private (Java) or never call it directly.
- 2Keep a static field for the single instance.
- 3Add a static get() that creates it once.
- 4Make creation thread-safe if threads are involved.
Watch it run
Step through the calls. Tap an arrow to jump to it.
Module A asks for the config. Nobody has created it yet.
Config.get()The code
Python and Java. Try writing the classes yourself from the diagram first.
class Config:
_instance = None
def __init__(self):
self.values = {"theme": "dark"}
@classmethod
def get(cls):
if cls._instance is None: # first call creates it
cls._instance = cls()
return cls._instance # every later call returns the same object
def demo():
first = Config.get()
second = Config.get()
first.values["theme"] = "light"
return first is second, second.values["theme"]What you gain
- One shared state, created once
- Lazy: built only if someone asks
What it costs
- It is global state in disguise: hard to test and easy to abuse
- Needs care with threads
- Prefer passing the one object in (dependency injection) when you can
You have already met it in
- Runtime in Java (Runtime.getRuntime())
- Spring beans are singletons by default
- A logger or a database connection pool
Often mixed up with
- FlyweightFlyweight shares many small objects; Singleton makes sure only one object of a class exists.
Make it stick
Build a Logger singleton whose messages list is shared by two classes. Then rewrite it by passing the logger into both classes and compare how easy each is to test.