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 i programmet, 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 (PHP)

Scenariot: En databasklass som garanterar att bara en anslutning skapas i hela applikationen.

<?php

final class Database
{
    private static ?Database $instance = null;
    private string $connectionString;

    // Privat konstruktor — ingen kan anropa new Database()
    private function __construct(string $connectionString)
    {
        $this->connectionString = $connectionString;
        echo "Databas initierad: {$connectionString}\n";
    }

    // Förhindra kloning
    private function __clone() {}

    // Statisk åtkomstmetod
    public static function getInstance(string $connectionString): static
    {
        if (static::$instance === null) {
            static::$instance = new static($connectionString);
        }
        return static::$instance;
    }

    public function query(string $sql): void
    {
        echo "[{$this->connectionString}] Kör: {$sql}\n";
    }
}

// ── Klientkod ─────────────────────────────────────────────

$db1 = Database::getInstance('mysql:host=localhost;dbname=bytebase');
$db2 = Database::getInstance('mysql:host=localhost;dbname=bytebase');

echo 'Samma instans? ' . ($db1 === $db2 ? 'ja' : 'nej') . "\n"; // ja

$db1->query('SELECT * FROM patterns');
$db2->query('INSERT INTO patterns VALUES (...)');
// Båda anropen går mot SAMMA anslutning

Output:

Databas initierad: mysql:host=localhost;dbname=bytebase
Samma instans? ja
[mysql:host=localhost;dbname=bytebase] Kör: SELECT * FROM patterns
[mysql:host=localhost;dbname=bytebase] Kör: INSERT INTO patterns VALUES (...)

Singleton i Laravel

I Laravel hanteras Singleton-beteende via Service Container. Du registrerar en klass som singleton i en Service Provider:

// AppServiceProvider.php
public function register(): void
{
    $this->app->singleton(Database::class, function () {
        return new Database('mysql:host=localhost;dbname=bytebase');
    });
}

Fördelar

  • Garanterar att en klass bara har en instans.
  • Global åtkomstpunkt till instansen.
  • Instansen initieras bara när den faktiskt behövs (lazy initialization).

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.
  • Moderna ramverk som Laravel hanterar detta bättre via Dependency Injection.

Av Victor Hernandez från Bytebase.se