Singleton (Singelton)
Introduktion
Singleton är ett skapande mönster som säkerställer att en klass endast har en instans och tillhandahåller en global åtkomstpunkt till den.
Problem
Singleton-mönstret löser två problem samtidigt:
- En enda instans — Viss kod kräver exakt en instans av en klass, t.ex. en databasanslutning. Skapar du flera instanser riskerar du inkonsekvent data och onödig resursåtgång.
- Global åtkomst — Objektet ska vara åtkomligt från var som helst, men skyddat från att skrivas över av misstag.
Lösning
- Privat konstruktor — Förhindrar att andra klasser skapar instanser med
new. - Statisk åtkomstmetod — Skapar instansen vid första anropet och returnerar den cachade instansen vid alla efterföljande anrop.
När ska Singleton användas?
- När en klass i ditt program bara ska ha en enda instans för alla klienter.
- T.ex. delad databasanslutning, logg-system, konfigurationshanterare.
Struktur
| Del | Beskrivning |
|---|---|
| Privat konstruktor | Hindrar direkt instansiering. |
| Statiskt privat fält | Lagrar den enda instansen. |
Statisk GetInstance() | Skapar (lazy) eller returnerar den befintliga instansen. |
Exempel — Databasanslutning (C# / .NET)
Scenariot: En databasklass som garanterar att bara en anslutning skapas i hela applikationen. I .NET används Lazy<T> för att uppnå thread-safe lazy initialization på ett elegant sätt.
public sealed class Database
{
// Lazy<T> garanterar thread-safe lazy initialization
private static readonly Lazy<Database> _lazy =
new(() => new Database("Server=localhost;Database=bytebase;"));
public static Database Instance => _lazy.Value;
public string ConnectionString { get; }
// Privat konstruktor — ingen kan anropa new Database()
private Database(string connectionString)
{
ConnectionString = connectionString;
Console.WriteLine($"Databas initierad: {connectionString}");
}
public void Query(string sql) =>
Console.WriteLine($"[{ConnectionString}] Kör: {sql}");
}
// ── Klientkod ─────────────────────────────────────────────
Database db1 = Database.Instance;
Database db2 = Database.Instance;
Console.WriteLine($"Samma instans? {ReferenceEquals(db1, db2)}"); // True
db1.Query("SELECT * FROM patterns");
db2.Query("INSERT INTO patterns VALUES (...)");
// Båda anropen går mot SAMMA anslutning
Output:
Databas initierad: Server=localhost;Database=bytebase;
Samma instans? True
[Server=localhost;Database=bytebase;] Kör: SELECT * FROM patterns
[Server=localhost;Database=bytebase;] Kör: INSERT INTO patterns VALUES (...)
Singleton i ASP.NET Core
I ASP.NET Core hanteras Singleton-beteende via det inbyggda Dependency Injection-systemet. Du registrerar tjänster med AddSingleton:
// Program.cs
builder.Services.AddSingleton<DatabaseService>();
// DatabaseService injiceras automatiskt — en instans i hela applikationens livstid
public class DatabaseService
{
public void Query(string sql) { /* ... */ }
}
Fördelar
- Garanterar att en klass bara har en instans.
Lazy<T>ger thread-safe lazy initialization utan manuell låsning.- Global åtkomstpunkt till instansen.
Nackdelar
- Kan dölja dålig design — komponenter som vet för mycket om varandra.
- Svårt att enhetstest — Singletons bär tillstånd mellan tester.
- I ASP.NET Core bör du föredra
AddSingletonvia DI framför manuell implementation.
Av Victor Hernandez från Bytebase.se