Singleton Pattern – controlul instanțelor unice în .NET
Printre cele mai populare modele de proiectare, Singleton Pattern ocupă un loc special. Este simplu de înțeles, dar și ușor de abuzat. Rolul său principal este să asigure că există o singură instanță a unei clase în întreaga aplicație și că oferă un punct global de acces la aceasta.
🧩 Ce este Singleton Pattern
Imaginați-vă o aplicație unde ai nevoie de un serviciu global, cum ar fi:
- 
un Logger, care scrie mesaje în consolă sau fișiere;
 - 
un ConfigManager, care păstrează setările aplicației;
 - 
o conexiune unică la o bază de date (în anumite scenarii simple).
 
Dacă fiecare clasă ar crea propriul logger sau propriul manager de configurare, am ajunge la instanțe multiple, inconsistente, greu de controlat.
Singleton rezolvă exact această problemă, asigurând o instanță unică și reutilizabilă în întreaga aplicație.
⚙️ Implementare simplă în C#
Cea mai clasică implementare arată așa:
public sealed class Logger
{
    private static Logger _instance;
    private static readonly object _lock = new();
private Logger() { }
    public static Logger Instance
    {
        get
        {
            lock (_lock)
            {
                return _instance ??= new Logger();
            }
        }
    }
    public void Log(string message)
    {
        Console.WriteLine($"[{DateTime.Now}] {message}");
    }
}
🔹 private Logger() — constructorul este privat, deci nu poți crea obiecte direct.
🔹 static Logger Instance — expune instanța unică.
🔹 lock (_lock) — asigură thread-safety, astfel încât instanța să fie creată o singură dată, chiar și în aplicații multi-threaded.
Utilizare:
Logger.Instance.Log("Aplicația a pornit!");
Logger.Instance.Log("Aceasta este o instanță unică.");
Indiferent de câte ori apelezi Instance, vei primi același obiect.
⚡ Variante moderne – Lazy Initialization
În .NET, putem simplifica și mai mult implementarea, folosind clasa Lazy<T>:
public sealed class Logger
{
    private static readonly Lazy<Logger> _instance =
        new(() => new Logger());
public static Logger Instance => _instance.Value;
private Logger() { }
    public void Log(string message)
    {
        Console.WriteLine($"[{DateTime.Now}] {message}");
    }
}
Avantajul?
Lazy<T> oferă inițializare sigură, thread-safe și creează instanța doar atunci când este nevoie.
💡 Când să folosești Singleton
✅ Cazuri potrivite:
- 
Servicii care trebuie să existe o singură dată (logging, caching, configurare).
 - 
Manageri globali de resurse (ex: API keys, fișiere de configurare).
 - 
Acces unificat la o componentă comună (ex: sistem de traduceri).
 
❌ Cazuri de evitat:
- 
Când obiectul are stare (stateful), iar acea stare se poate schimba în timp.
 - 
În testare unit, deoarece Singleton-urile pot fi greu de mock-uit.
 - 
În aplicații mari, pot crea dependențe ascunse și îngreunează refactorizarea.
 
🧠 Alternativa: Dependency Injection
În ecosistemul modern .NET, multe dintre cazurile Singleton pot fi rezolvate mai elegant cu Dependency Injection (DI):
Astfel, cadrul (ASP.NET, Blazor etc.) gestionează durata de viață a instanței.
Obiectul este tot unic, dar fără a încălca principiile de inversare a dependențelor.
Singleton devine astfel un concept logic, nu o implementare hard-coded.
🚀 Concluzie
Singleton Pattern este un model simplu, dar puternic, atunci când este folosit corect.
El asigură o instanță unică și acces global, reducând consumul de resurse și menținând consistența aplicației.
Totuși, trebuie folosit cu grijă: un Singleton prost implementat poate duce la cod rigid, greu de testat și de întreținut.
Folosit corect, în combinație cu Dependency Injection, rămâne unul dintre pilonii arhitecturii moderne .NET.
