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 InstanzDie 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.
Aufgabe
EinfachLassen 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. IhreAppConfigsollte Folgendes enthalten:Ein privates statisches Feld, um die einzige Instanz zu halten.
Ein privates String-Feld namens
appNameund ein privates int-Feld namensmaxUsers.Einen privaten Konstruktor, der
appNameauf"MyApplication"undmaxUsersauf100initialisiert und dannAppConfig initializedausgibt.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, dieApp: [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
AppConfigund speichern Sie diese in einer Variablen namensconfig1. Rufen SiedisplaySettings()auf, um die Standardwerte anzuzeigen.Aktualisieren Sie dann die Einstellungen mit Ihren Eingabewerten, indem Sie
setAppNameundsetMaxUsersaufconfig1aufrufen.Holen Sie sich eine weitere Instanz und speichern Sie diese in
config2. Rufen SiedisplaySettings()aufconfig2auf, um zu beweisen, dass sie die überconfig1vorgenommenen Änderungen widerspiegelt.Geben Sie schließlich aus, ob beide Referenzen auf dasselbe Objekt zeigen:
Same instance: [true/false], indem Sieconfig1 == config2vergleichen.
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 staticFeld, das die einzige Instanz hält - Ein
privateKonstruktor, um eine direkte Instanziierung zu verhindern - Eine
public staticMethode, 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 InstanzDie 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
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenFelder (Attribute)Konstruktor-MethodeKonstruktor-ÜberladungRückblick – Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (extends)Das super-SchlüsselwortMethoden überschreiben (@Override)Konstruktor-VerkettungDie Object-KlasseEinfache & mehrstufige VererbungWarum keine Mehrfachvererbung von KlassenZusammenfassung - Mitarbeiter-Hierarchie7Spezielle Methoden & Object-Klasse
toString()-Methodeequals() und hashCode()clone()-MethodecompareTo() und ComparableComparator-InterfaceZusammenfassung – Benutzerdefiniertes Sortieren2Zugriffsmodifikatoren & Kapselung
Überblick über ZugriffsebenenGetter- und Setter-MethodenInformation HidingDas final-SchlüsselwortRückblick – Bankkonto-Manager5Polymorphismus
Grundlagen der MethodenüberladungMethodenüberschreibung (Laufzeit)Upcasting und DowncastingDer instanceof-OperatorAbstrakte Klassen und MethodenZusammenfassung – Formenrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungAggregation vs. KompositionInnere, geschachtelte & anonyme KlassenEnums und Enum-MethodenRecords (Java 16+)Sealed Classes (Java 17+)11Design Patterns Teil 1
Einführung in Design PatternsSingleton-PatternFactory-PatternBuilder-PatternObserver-PatternStrategy-Pattern14Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung3Klasseneigenschaften & Statische Member
Instanz- vs. statische VariablenStatische MethodenStatische BlöckeKonstanten (static final)Zusammenfassung – Zähler & Utility6Interfaces & Abstrakte Klassen
Einführung in InterfacesInterfaces implementierenImplementierung mehrerer InterfacesDefault- & statische Methoden in InterfacesAbstrakte Klassen vs. InterfacesFunktionale InterfacesZusammenfassung - Zahlungssystem