Menu
Coddy logo textTech

Singleton-Muster

Teil des Abschnitts Objektorientierte Programmierung der PHP-Journey von Coddy — Lektion 71 von 91.

Das Singleton-Muster ist ein Erzeugungsmuster, das sicherstellt, dass eine Klasse während des gesamten Lebenszyklus Ihrer Anwendung nur eine einzige Instanz hat. Es bietet zudem einen globalen Zugriffspunkt auf diese Instanz.

Dieses Muster ist nützlich, wenn Sie genau ein Objekt benötigen, um Aktionen im gesamten System zu koordinieren, wie zum Beispiel eine Datenbankverbindung, einen Konfigurationsmanager oder einen Logger. Das Erstellen mehrerer Instanzen davon würde Ressourcen verschwenden oder zu inkonsistentem Verhalten führen.

So implementieren Sie ein Singleton in PHP:

<?php
class Database {
    private static ?Database $instance = null;
    
    private function __construct() {
        // Privater Konstruktor verhindert die direkte Instanziierung
    }
    
    public static function getInstance(): Database {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    public function query(string $sql): string {
        return "Executing: $sql";
    }
}

$db1 = Database::getInstance();
$db2 = Database::getInstance();

echo $db1 === $db2 ? "Same instance" : "Different instances";

Ausgabe:

Same instance

Das Muster beruht auf drei Schlüsselelementen: einem privaten Konstruktor, der die direkte Verwendung von new verhindert, einer statischen Eigenschaft, welche die einzige Instanz hält, und einer statischen Methode, die die Instanz beim ersten Aufruf erstellt und bei nachfolgenden Aufrufen zurückgibt.

Um das Singleton wirklich sicher zu machen, sollten Sie auch das Klonen und Deserialisieren verhindern:

<?php
private function __clone() {}
public function __wakeup() {
    throw new Exception("Cannot unserialize singleton");
}
challenge icon

Aufgabe

Einfach

Lassen Sie uns einen Konfigurationsmanager mit dem Singleton-Pattern erstellen. In realen Anwendungen möchte man in der Regel eine einzige "Source of Truth" für Konfigurationseinstellungen haben — mehrere Konfigurationsobjekte könnten zu inkonsistentem Verhalten in Ihrem System führen.

Sie werden Ihren Code auf zwei Dateien aufteilen:

  • Config.php — Erstellen Sie eine Config-Klasse, die das Singleton-Pattern implementiert. Ihr Konfigurationsmanager sollte:
    • Einstellungen in einer privaten Array-Eigenschaft speichern
    • Einen privaten Konstruktor haben, der das Einstellungs-Array als leer initialisiert
    • Eine statische getInstance()-Methode bereitstellen, die die einzige Instanz zurückgibt
    • Das Klonen verhindern, indem __clone() privat gemacht wird
    • Eine set(string $key, string $value): void-Methode enthalten, um eine Einstellung zu speichern
    • Eine get(string $key): ?string-Methode enthalten, die den Wert für einen Schlüssel zurückgibt, oder null, wenn der Schlüssel nicht existiert
    • Eine getAll(): array-Methode enthalten, die alle Einstellungen zurückgibt
  • main.php — Binden Sie die Config-Datei ein. Sie erhalten zwei Eingaben: einen Schlüssel und einen Wert.

    Holen Sie sich die Config-Instanz und setzen Sie eine Standardeinstellung mit dem Schlüssel "app_name" und dem Wert "MyApp".

    Holen Sie sich eine zweite Referenz auf die Config-Instanz (indem Sie erneut getInstance() aufrufen) und verwenden Sie diese, um eine weitere Einstellung mit dem Schlüssel und dem Wert aus den Eingaben zu setzen.

    Überprüfen Sie schließlich, ob das Singleton korrekt funktioniert:

    • Geben Sie "Same instance: true" oder "Same instance: false" aus, je nachdem, ob beide Referenzen auf dasselbe Objekt zeigen (verwenden Sie === zum Vergleich)
    • Geben Sie "Settings count: [count]" aus, um die Gesamtzahl der gespeicherten Einstellungen anzuzeigen (verwenden Sie count() auf das Ergebnis von getAll())
    • Geben Sie "App name: [value]" aus, um die Einstellung app_name anzuzeigen, die über die zweite Referenz abgerufen wurde

Diese Herausforderung demonstriert den Kernvorteil des Singleton-Patterns: Egal wie oft Sie getInstance() aufrufen, Sie erhalten immer dasselbe Objekt, wodurch sichergestellt wird, dass alle Teile Ihrer Anwendung denselben Konfigurationszustand teilen.

Spickzettel

Das Singleton-Pattern stellt sicher, dass eine Klasse während des gesamten Lebenszyklus Ihrer Anwendung nur eine einzige Instanz hat, und bietet einen globalen Zugriffspunkt auf diese Instanz.

Dieses Muster ist nützlich für Objekte wie Datenbankverbindungen, Konfigurationsmanager oder Logger, bei denen mehrere Instanzen Ressourcen verschwenden oder inkonsistentes Verhalten verursachen würden.

Grundlegende Singleton-Implementierung:

<?php
class Database {
    private static ?Database $instance = null;
    
    private function __construct() {
        // Private constructor prevents direct instantiation
    }
    
    public static function getInstance(): Database {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

$db1 = Database::getInstance();
$db2 = Database::getInstance();

echo $db1 === $db2 ? "Same instance" : "Different instances";
// Output: Same instance

Kernelemente:

  • Privater Konstruktor — verhindert die direkte Verwendung von new
  • Statische Eigenschaft — hält die einzelne Instanz
  • Statische Methode — erstellt die Instanz beim ersten Aufruf und gibt sie bei nachfolgenden Aufrufen zurück

Verhindern von Klonen und Deserialisierung:

<?php
private function __clone() {}

public function __wakeup() {
    throw new Exception("Cannot unserialize singleton");
}

Probier es selbst

<?php

require_once 'Config.php';

// Eingabe lesen
$key = trim(fgets(STDIN));
$value = trim(fgets(STDIN));

// TODO: Die Config-Instanz abrufen und "app_name" auf "MyApp" setzen

// TODO: Eine zweite Referenz auf Config mit getInstance() erneut abrufen

// TODO: Die zweite Referenz verwenden, um die Einstellung mit $key und $value aus der Eingabe zu setzen

// TODO: Überprüfen, ob Singleton funktioniert:
// - "Same instance: true" oder "Same instance: false" basierend auf dem === Vergleich ausgeben
// - "Settings count: [count]" mit der Gesamtzahl der Einstellungen ausgeben
// - "App name: [value]" mit dem über die zweite Referenz abgerufenen app_name ausgeben

?>
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung