Menu
Coddy logo textTech

Singleton-Pattern

Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy — Lektion 65 von 87.

Das Singleton-Pattern ist ein Erzeugungsmuster, das sicherstellt, dass eine Klasse während der gesamten Anwendung nur eine Instanz hat, während es einen globalen Zugriffspunkt auf diese Instanz bietet. Dies ist nützlich für Ressourcen, die gemeinsam genutzt werden sollten, wie Konfigurationsmanager, Verbindungspools oder Protokollierungsdienste.

Das Muster funktioniert, indem der Konstruktor privat gemacht wird, was eine direkte Instanziierung verhindert. Stattdessen steuert eine statische Methode den Zugriff auf die einzelne Instanz:

public class DatabaseConnection {
    private static DatabaseConnection instance;
    
    // Privater Konstruktor verhindert die direkte Instanziierung
    private DatabaseConnection() {
        System.out.println("Connection created");
    }
    
    // Globaler Zugriffspunkt
    public static DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
    
    public void query(String sql) {
        System.out.println("Executing: " + sql);
    }
}

Jeder Aufruf von getInstance() gibt dasselbe Objekt zurück. Die Instanz wird erst beim ersten Anfordern erstellt – dies wird als verzögerte Initialisierung (lazy initialization) bezeichnet:

DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();

System.out.println(db1 == db2);  // true - dieselbe Instanz

Die Hauptkomponenten eines Singletons sind: ein private static Feld, das die Instanz hält, ein private Konstruktor und eine public static Methode, welche die Instanz zurückgibt. Diese Kombination garantiert, dass man, egal wie oft man das Objekt anfordert, immer dasselbe erhält.

challenge icon

Aufgabe

Einfach

Lassen Sie uns einen Konfigurationsmanager für eine Anwendung mit dem Singleton-Muster erstellen! Ihr Konfigurationsmanager stellt sicher, dass alle Teile Ihrer Anwendung dieselben Einstellungen teilen, egal wie oft sie Zugriff darauf anfordern.

Sie werden Ihren Code auf zwei Dateien verteilen:

  • AppConfig.java: Erstellen Sie eine Singleton-Klasse, die die Konfigurationseinstellungen der Anwendung verwaltet. Ihre AppConfig sollte Folgendes enthalten:

    Ein privates statisches Feld, um die einzige Instanz zu halten.

    Ein privates String-Feld namens appName und ein privates int-Feld namens maxUsers.

    Einen privaten Konstruktor, der appName auf "MyApplication" und maxUsers auf 100 initialisiert und dann AppConfig initialized ausgibt.

    Eine öffentliche statische getInstance()-Methode, die die Instanz nur erstellt, wenn sie noch nicht existiert (Lazy Initialization), und sie dann zurückgibt.

    Eine setAppName(String name)-Methode, die den Anwendungsnamen aktualisiert.

    Eine setMaxUsers(int max)-Methode, die die maximale Anzahl der Benutzer aktualisiert.

    Eine displaySettings()-Methode, die App: [appName], Max Users: [maxUsers] ausgibt.

  • Main.java: Demonstrieren Sie, dass Ihr Singleton korrekt funktioniert! Sie erhalten zwei Eingaben: einen neuen Anwendungsnamen (String) und einen neuen Wert für die maximale Benutzeranzahl (Integer).

    Holen Sie sich zuerst eine Instanz von AppConfig und speichern Sie diese in einer Variablen namens config1. Rufen Sie displaySettings() auf, um die Standardwerte anzuzeigen.

    Aktualisieren Sie dann die Einstellungen mit Ihren Eingabewerten, indem Sie setAppName und setMaxUsers auf config1 aufrufen.

    Holen Sie sich eine weitere Instanz und speichern Sie diese in config2. Rufen Sie displaySettings() auf config2 auf, um zu beweisen, dass sie die über config1 vorgenommenen Änderungen widerspiegelt.

    Geben Sie schließlich aus, ob beide Referenzen auf dasselbe Objekt zeigen: Same instance: [true/false], indem Sie config1 == config2 vergleichen.

Sie erhalten zwei Eingaben in der folgenden Reihenfolge: den neuen Anwendungsnamen (String) und die neue maximale Benutzeranzahl (Integer).

Diese Herausforderung demonstriert den Kernvorteil des Singleton-Musters: Egal wie oft Sie die Konfiguration anfordern, Sie erhalten immer dieselbe Instanz mit demselben gemeinsamen Zustand!

Spickzettel

Das Singleton-Muster stellt sicher, dass eine Klasse während der gesamten Anwendung nur eine Instanz hat, und bietet einen globalen Zugriffspunkt auf diese Instanz.

Kernkomponenten:

  • Ein private static Feld, das die einzige Instanz hält
  • Ein private Konstruktor, um eine direkte Instanziierung zu verhindern
  • Eine public static Methode, die die Instanz zurückgibt

Grundlegende Implementierung:

public class DatabaseConnection {
    private static DatabaseConnection instance;
    
    // Privater Konstruktor verhindert direkte Instanziierung
    private DatabaseConnection() {
        System.out.println("Connection created");
    }
    
    // Globaler Zugriffspunkt
    public static DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
    
    public void query(String sql) {
        System.out.println("Executing: " + sql);
    }
}

Verwendung:

DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();

System.out.println(db1 == db2);  // true - dieselbe Instanz

Die Instanz wird erst erstellt, wenn sie zum ersten Mal angefordert wird – dies wird als Lazy Initialization (verzögerte Initialisierung) bezeichnet. Jeder Aufruf von getInstance() gibt dasselbe Objekt zurück, wodurch ein gemeinsamer Zustand in der gesamten Anwendung sichergestellt wird.

Probier es selbst

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Eingaben lesen
        String newAppName = scanner.nextLine();
        int newMaxUsers = scanner.nextInt();
        
        // TODO: Erste Instanz von AppConfig abrufen und in config1 speichern
        
        // TODO: displaySettings() auf config1 aufrufen, um Standardwerte anzuzeigen
        
        // TODO: Einstellungen mit setAppName und setMaxUsers auf config1 aktualisieren
        
        // TODO: Eine weitere Instanz abrufen und in config2 speichern
        
        // TODO: displaySettings() auf config2 aufrufen, um zu beweisen, dass Änderungen geteilt werden
        
        // TODO: Ausgeben, ob beide Referenzen auf dasselbe Objekt zeigen:
        // "Same instance: [true/false]" unter Verwendung von config1 == config2
    }
}
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