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:

  1. 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.
  2. 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

DelBeskrivning
Privat konstruktorHindrar direkt instansiering.
Statiskt privat fältLagrar 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 AddSingleton via DI framför manuell implementation.

Av Victor Hernandez från Bytebase.se