Singleton Pattern
Published on: October 06, 2025
Tags: #singleton #design-patterns
The Classic Singleton Pattern
classDiagram %% Adding a stereotype to make the pattern role explicit class Singleton { <> -_instance: Singleton$ -Singleton() +getInstance(): Singleton$ } %% The note remains the same and is still very helpful note for Singleton "getInstance() logic:\nif _instance is null:\n _instance = new Singleton()\nreturn _instance" class ClientA { -singleton_instance: Singleton +do_work() } class ClientB { -singleton_instance: Singleton +do_something_else() } ClientA ..> Singleton : uses ClientB ..> Singleton : uses
The Anti-Pattern Problem: Global Shared State
graph TD %% All relationships are now grouped inside the subgraph subgraph Application direction TB ModuleA -->|Reads/Writes to| Singleton["Singleton (Global State)"]; ModuleB -->|Reads/Writes to| Singleton; ModuleC -->|Reads from| Singleton; ModuleA -.-> |Invisible Coupling| ModuleB; ModuleB -.-> |Invisible Coupling| ModuleC; end %% Added styling for the modules to match the image's purple style ModuleA fill:#e6e6fa,stroke:#333,stroke-width:2px style ModuleB fill:#e6e6fa,stroke:#333,stroke-width:2px style ModuleC fill:#e6e6fa,stroke:#333,stroke-width:2px %% Kept the original styling for the Singleton style Singleton fill:#f9f,stroke:#333,stroke-width:2px
The Multi-threading Race Condition
sequenceDiagram participant ThreadA participant ThreadB participant SingletonClass par ThreadA->>SingletonClass: getInstance() activate SingletonClass note over ThreadA, SingletonClass: Checks if instance exists (it's null) and ThreadB->>SingletonClass: getInstance() activate SingletonClass note over ThreadB, SingletonClass: Checks if instance exists (it's null) end note right of SingletonClass: Both threads see a null instance before either can create one. par ThreadA->>SingletonClass: Creates new Instance A SingletonClass-->>ThreadA: Returns Instance A deactivate SingletonClass and ThreadB->>SingletonClass: Creates new Instance B SingletonClass-->>ThreadB: Returns Instance B deactivate SingletonClass end note right of SingletonClass: Problem! Two instances are created.
The "Pythonic" Singleton: A Module
graph TD subgraph YourApp direction TB A[main_app.py] -->|import config| C["config.py (Module Singleton)"]; B[some_other_module.py] -->|import config| C; end style C fill:#ccf,stroke:#333,stroke-width:2px
The Real Reason: Controlled Instantiation (Lazy Loading)
sequenceDiagram participant Client participant predict_function participant ModelLoader Client->>predict_function: Make prediction activate predict_function predict_function->>ModelLoader: Get Instance activate ModelLoader note over ModelLoader: First call: instance does not exist. ModelLoader->>ModelLoader: __init__() - Loading large model... ModelLoader-->>predict_function: Return new instance deactivate ModelLoader predict_function->>ModelLoader: instance.predict() predict_function-->>Client: Return prediction deactivate predict_function Client->>predict_function: Make another prediction activate predict_function predict_function->>ModelLoader: Get Instance activate ModelLoader note over ModelLoader: Instance already exists, return it directly. ModelLoader-->>predict_function: Return existing instance deactivate ModelLoader predict_function->>ModelLoader: instance.predict() predict_function-->>Client: Return prediction deactivate predict_function
Related Pattern: The Object Pool
sequenceDiagram participant Client participant ObjectPool participant DB_Connection Client->>ObjectPool: acquireConnection() activate ObjectPool alt Pool has an available connection ObjectPool-->>Client: return existing_connection else Pool is empty and not at max capacity ObjectPool->>DB_Connection: new() activate DB_Connection DB_Connection-->>ObjectPool: new_connection deactivate DB_Connection ObjectPool-->>Client: return new_connection end deactivate ObjectPool Note over Client: Client uses the connection... Client->>ObjectPool: releaseConnection(connection) activate ObjectPool Note over ObjectPool: Connection is returned to the pool for reuse. deactivate ObjectPool