Singleton-Muster
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 90 von 110.
Das Singleton-Muster stellt sicher, dass eine Klasse während der gesamten Anwendung nur eine einzige Instanz hat, und bietet einen globalen Zugriffspunkt darauf. Dies ist nützlich, wenn genau ein Objekt benötigt wird, um Aktionen im gesamten System zu koordinieren – wie ein Konfigurationsmanager, eine Datenbankverbindung oder ein Protokollierungsdienst.
In Dart können Sie ein Singleton mithilfe eines Factory-Konstruktors in Kombination mit einer privaten statischen Instanz implementieren:
class AppConfig {
static final AppConfig _instance = AppConfig._internal();
String appName = 'MyApp';
String version = '1.0.0';
// Privater benannter Konstruktor
AppConfig._internal();
// Factory-Konstruktor gibt dieselbe Instanz zurück
factory AppConfig() {
return _instance;
}
}
void main() {
var config1 = AppConfig();
var config2 = AppConfig();
config1.appName = 'UpdatedApp';
print(config2.appName); // UpdatedApp
print(identical(config1, config2)); // true
}Der private Konstruktor _internal() verhindert die externe Instanziierung. Das statische Feld _instance hält die einzige Instanz, die einmalig beim Laden der Klasse erstellt wird. Jeder Aufruf von AppConfig() gibt dieselbe Instanz zurück, sodass Änderungen, die über eine Referenz vorgenommen werden, überall sichtbar sind.
Verwenden Sie Singletons sparsam – sie führen einen globalen Zustand ein, was das Testen erschweren kann. Sie eignen sich am besten für wirklich gemeinsam genutzte Ressourcen, die einen konsistenten Zustand über Ihre gesamte Anwendung hinweg erfordern.
Aufgabe
EinfachLass uns einen Manager für Spieleinstellungen mit dem Singleton-Muster erstellen! Du wirst ein System entwickeln, das sicherstellt, dass während der gesamten Anwendung nur eine Instanz der Spieleinstellungen existiert, sodass alle Teile deines Spiels dieselbe Konfiguration teilen.
Du wirst deinen Code in zwei Dateien organisieren:
game_settings.dart: Erstelle eineGameSettings-Klasse, die das Singleton-Muster implementiert. Dein Einstellungsmanager sollte Folgendes haben:- Eine private statische finale Instanz, die das einzige
GameSettings-Objekt hält - Einen privaten benannten Konstruktor, um eine externe Instanziierung zu verhindern
- Einen Factory-Konstruktor, der immer dieselbe Instanz zurückgibt
- Drei Einstellungsfelder:
difficulty(String, Standard: "Normal"),soundEnabled(bool, Standard: true) undplayerName(String, Standard: "Player1") - Eine Methode
displaySettings(), welche die aktuellen Einstellungen in diesem Format ausgibt:Settings: [playerName] | Difficulty: [difficulty] | Sound: [on/off](gib "on" aus, wenn soundEnabled true ist, "off", wenn false)
- Eine private statische finale Instanz, die das einzige
main.dart: Importiere deine Spieleinstellungen und demonstriere, dass das Singleton korrekt funktioniert:- Hole die Einstellungsinstanz und nenne sie
settings1 - Rufe
displaySettings()auf, um die Standardwerte anzuzeigen - Ändere die Einstellungen: Ändere
playerNamezu "Hero",difficultyzu "Hard" undsoundEnabledzu false - Hole eine weitere Referenz auf die Einstellungen und nenne sie
settings2 - Rufe
displaySettings()fürsettings2auf, um zu beweisen, dass sie die übersettings1vorgenommenen Änderungen widerspiegelt - Gib aus, ob
settings1undsettings2identisch sind, unter Verwendung von:Same instance: [true/false]
- Hole die Einstellungsinstanz und nenne sie
Dies demonstriert den Hauptvorteil von Singletons – Änderungen, die irgendwo in deiner Anwendung vorgenommen werden, sind sofort überall sonst sichtbar, da es nur eine Instanz gibt!
Erwartete Ausgabe:
Settings: Player1 | Difficulty: Normal | Sound: on
Settings: Hero | Difficulty: Hard | Sound: off
Same instance: trueSpickzettel
Das Singleton-Muster stellt sicher, dass eine Klasse während der gesamten Anwendung nur eine einzige Instanz hat, und bietet einen globalen Zugriffspunkt darauf.
In Dart implementieren Sie ein Singleton mit einem Factory-Konstruktor in Kombination mit einer privaten statischen Instanz:
class AppConfig {
static final AppConfig _instance = AppConfig._internal();
String appName = 'MyApp';
String version = '1.0.0';
// Privater benannter Konstruktor
AppConfig._internal();
// Factory-Konstruktor gibt dieselbe Instanz zurück
factory AppConfig() {
return _instance;
}
}Schlüsselkomponenten:
static final _instance: Hält die einzelne Instanz, die einmal beim Laden der Klasse erstellt wird_internal(): Privater Konstruktor verhindert externe Instanziierungfactory AppConfig(): Gibt jedes Mal dieselbe Instanz zurück
Anwendungsbeispiel:
var config1 = AppConfig();
var config2 = AppConfig();
config1.appName = 'UpdatedApp';
print(config2.appName); // UpdatedApp
print(identical(config1, config2)); // trueÄnderungen, die über eine Referenz vorgenommen werden, sind überall sichtbar, da alle Referenzen auf dieselbe Instanz zeigen.
Probier es selbst
import 'game_settings.dart';
void main() {
// TODO: Hol dir die settings-Instanz und nenne sie settings1
// TODO: Rufe displaySettings() auf, um die Standardwerte anzuzeigen
// TODO: Ändere die Einstellungen:
// - Ändere playerName in "Hero"
// - Ändere difficulty in "Hard"
// - Ändere soundEnabled in false
// TODO: Hol dir eine weitere Referenz auf settings und nenne sie settings2
// TODO: Rufe displaySettings() für settings2 auf, um zu beweisen, dass es die Änderungen widerspiegelt
// TODO: Gib aus, ob settings1 und settings2 identisch sind
// Format: Same instance: [true/false]
}
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 DateienBibliotheken & ImportsEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenInstanzvariablenKonstruktor-GrundlagenWiederholung – Einfacher Taschenrechner4Null-Sicherheit
Einführung in Null-SicherheitNullable vs. Non-NullableDie ? und ! OperatorenLate-Schlüsselwort & Null-SicherheitNull-aware OperatorenNull-Sicherheit in KlassenZusammenfassung – Benutzerprofil-System7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrfache InterfacesRückblick – Shape Calculator10Collections & Generics
Überblick über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische ConstraintsIterable & IteratorZusammenfassung – Generische Speicherung13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
Standard-KonstruktorBenannte KonstruktorenInitialisierungslistenKonstante KonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenZusammenfassung – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _ Präfix-KonventionSichtbarkeit auf Library-EbeneGetter & Setter im DetailInformation HidingRückblick – Schülerdatensätze8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas on-Schlüsselwort in MixinsMixin vs. VererbungMixin vs. InterfaceZusammenfassung - Tiersystem11Spezielle Methoden
toString() überschreibenhashCode & == überschreibenComparable-Interfacecall()-MethodenoSuchMethod überschreibenZusammenfassung – Eigene Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster3Klasseneigenschaften
Instanz- vs. statische MemberFinal & Const FelderLate-VariablenStatische Methoden & FelderGetter und SetterZusammenfassung – Bankkonto-Manager6Vererbung
Grundlagen der VererbungDas super-SchlüsselwortMethoden überschreibenDie @override-AnnotationDas final-Klassen-SchlüsselwortKonstruktoren & VererbungRückblick – Mitarbeiter-Hierarchie9Polymorphismus
Polymorphismus-GrundlagenPolymorphismus über InterfacesTypprüfung zur LaufzeitDie is & as OperatorenDas covariant SchlüsselwortZusammenfassung – Payment Processor