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 kannst du 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 Instanziierung von außen. Das statische Feld _instance enthält die einzige Instanz, die einmalig geladen wird, wenn die Klasse geladen 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 Spiel-Einstellungsmanager mithilfe des Singleton-Musters erstellen! Du erstellst ein System, das sicherstellt, dass während der gesamten Anwendung nur eine Instanz der Spieleinstellungen existiert, sodass alle Teile deines Spiels dieselbe Konfiguration verwenden.
Du organisierst deinen Code in zwei Dateien:
game_settings.dart: Erstelle eineGameSettings-Klasse, die das Singleton-Muster implementiert. Dein Einstellungsmanager sollte Folgendes enthalten:- Eine private statische finale Instanz, die das einzige
GameSettings-Objekt enthält - Einen privaten benannten Konstruktor, um eine Instanziierung von außen zu verhindern
- Einen Factory-Konstruktor, der immer dieselbe Instanz zurückgibt
- Drei Einstellungsfelder:
difficulty(String, Standardwert: "Normal"),soundEnabled(bool, Standardwert: true) undplayerName(String, Standardwert: "Player1") - Eine Methode
displaySettings(), die die aktuellen Einstellungen in diesem Format ausgibt:Settings: [playerName] | Difficulty: [difficulty] | Sound: [on/off](gib "on" aus, wenn soundEnabled true ist, und "off", wenn es false ist)
- Eine private statische finale Instanz, die das einzige
main.dart: Importiere deine Spieleinstellungen und zeige, dass das Singleton korrekt funktioniert:- Rufe die Einstellungsinstanz ab und nenne sie
settings1 - Rufe
displaySettings()auf, um die Standardwerte anzuzeigen - Ändere die Einstellungen: Setze
playerNameauf "Hero",difficultyauf "Hard" undsoundEnabledauf false - Rufe eine weitere Referenz auf die Einstellungen ab und nenne sie
settings2 - Rufe
displaySettings()fürsettings2auf, um zu beweisen, dass die übersettings1vorgenommenen Änderungen widergespiegelt werden - Gib aus, ob
settings1undsettings2identisch sind, und verwende dafür:Same instance: [true/false]
- Rufe die Einstellungsinstanz ab und nenne sie
Dies zeigt den Hauptvorteil von Singletons: Änderungen, die an beliebiger Stelle in deiner Anwendung vorgenommen werden, sind sofort überall sichtbar, weil es nur eine Instanz gibt!
Erwartete Ausgabe:
Settings: Player1 | Difficulty: Normal | Sound: on
Settings: Hero | Difficulty: Hard | Sound: off
Same instance: trueProbier 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 Schlüsselwort thisMethodenInstanzvariablenGrundlagen von KonstruktorenRückblick – Einfacher Taschenrechner4Null-Sicherheit
Einführung in die Null-SicherheitNullable vs. Non-NullableDie Operatoren ? und !Late-Schlüsselwort und Null-SicherheitNull-bewusste OperatorenNull-Sicherheit in KlassenRückblick – Benutzers профilsystem7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrere InterfacesZusammenfassung – Formrechner10Collections & Generics
Übersicht über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische EinschränkungenIterable & IteratorRückblick – Generischer Speicher13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungErweiterungsmethodenAufrufbare KlassenVersiegelte Klassen (Dart 3)Records (Dart 3)Muster & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
StandardkonstruktorBenannte KonstruktorenInitialisierungslistenKonstantenkonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenRückblick – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _-Präfix-KonventionBibliotheksweite SichtbarkeitTiefergehende Getter & SetterInformationsverbergungRückblick – Studierendendaten8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas Schlüsselwort „on“ in MixinsMixin vs. VererbungMixin vs. SchnittstelleRückblick – Tiersystem11Spezielle Methoden
toString()-ÜberschreibunghashCode- & ==-ÜberschreibungComparable-Schnittstellecall()-MethodenoSuchMethod-ÜberschreibungWiederholung – Benutzerdefinierte Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFabrikmusterBeobachtermusterStrategiemuster17Abschließende Herausforderungen
E-Learning-PlattformBankingsystemSpielcharaktersystemFahrzeugvermietung3Klassen-Eigenschaften
Instanz- vs. statische MemberFinal- und Const-FelderLate-VariablenStatische Methoden und FelderGetter und SetterRückblick – Bankkontoverwaltung6Vererbung
Grundlegende VererbungDas Schlüsselwort superMethodenüberschreibungDie Annotation @overrideDas Schlüsselwort final für KlassenKonstruktoren und VererbungZusammenfassung – Mitarbeiterhierarchie9Polymorphismus
Grundlagen des PolymorphismusPolymorphismus über InterfacesLaufzeit-TypüberprüfungDie Operatoren is & asCovariant-SchlüsselwortZusammenfassung – ZahlungsprozessorÜbe selbstständig: Online-Dart-Compiler