Ein Entwurfsmuster ist eine Vorlage, die ein häufig wiederkehrendes Problem im Softwaredesign löst.
Das Zustandsmuster ist ein Verhaltensmuster, das es einem Objekt ermöglicht, sein Verhalten zu ändern, wenn sich sein interner Zustand ändert.
Hier erfahren Sie, wie Sie das Zustandsmuster in TypeScript verwenden.
Was ist das Zustandsmuster?
Das Zustandsentwurfsmuster ist eng verwandt mit einem endlichen Automaten, der ein Programm beschreibt, das in einem existiert endlich Anzahl von Staaten zu einem bestimmten Zeitpunkt und verhält sich innerhalb jedes Staates anders.
Es gibt begrenzte, vorgegebene Regeln – Übergänge – die die anderen Zustände regeln, zu denen jeder Zustand wechseln kann.
Zum Kontext: Wenn die Einkaufsbestellung eines Kunden in einem Online-Shop „zugestellt“ wurde, kann sie nicht „storniert“ werden, da sie bereits „zugestellt“ wurde. „Zugestellt“ und „Storniert“ sind endliche Status der Bestellung, und die Bestellung verhält sich je nach Status unterschiedlich.
Das Zustandsmuster
erstellt eine Klasse für jeden möglichen Zustand, wobei in jeder Klasse ein zustandsspezifisches Verhalten enthalten ist.Eine beispielhafte zustandsbasierte Anwendung
Angenommen, Sie erstellen eine Anwendung, die den Status eines Artikels für ein Verlagsunternehmen verfolgt. Ein Artikel kann entweder auf Genehmigung warten, von einem Autor entworfen, von einem Redakteur bearbeitet oder veröffentlicht werden. Dies sind die endlichen Zustände eines zu veröffentlichenden Artikels; Innerhalb jedes eindeutigen Zustands verhält sich der Artikel anders.
Mit dem folgenden Zustandsdiagramm können Sie die verschiedenen Zustände und Übergänge der Artikelanwendung visualisieren:
Um dieses Szenario im Code zu implementieren, müssten Sie zuerst eine Schnittstelle für den Artikel deklarieren:
SchnittstelleArtikelSchnittstelle{
Tonhöhe(): Leere;
Entwurf(): Leere;
bearbeiten(): Leere;
veröffentlichen(): Leere;
}
Diese Schnittstelle hat alle möglichen Zustände der Anwendung.
Erstellen Sie als Nächstes eine Anwendung, die alle Schnittstellenmethoden implementiert:
// Anwendung
KlasseArtikelimplementiertArtikelSchnittstelle{
Konstrukteur() {
Das.showCurrentState();
}PrivatgeländeaktuellenZustand anzeigen(): Leere{
//...
}öffentlichTonhöhe(): Leere{
//...
}öffentlichEntwurf(): Leere{
//...
}öffentlichbearbeiten(): Leere{
//...
}
öffentlichveröffentlichen(): Leere{
//...
}
}
Das private aktuellenZustand anzeigen Methode ist eine Utility-Methode. Dieses Tutorial verwendet es, um zu zeigen, was in jedem Zustand passiert. Es ist kein erforderlicher Teil des Zustandsmusters.
Umgang mit Zustandsübergängen
Als Nächstes müssen Sie die Zustandsübergänge behandeln. Die Handhabung des Zustandsübergangs in Ihrer Anwendungsklasse würde viele erfordern bedingte Aussagen. Dies würde zu sich wiederholendem Code führen, der schwerer zu lesen und zu warten ist. Um dieses Problem zu lösen, können Sie die Übergangslogik für jeden Zustand an eine eigene Klasse delegieren.
Bevor Sie jede Zustandsklasse schreiben, sollten Sie eine abstrakte Basisklasse erstellen, um sicherzustellen, dass jede Methode, die in einem ungültigen Zustand aufgerufen wird, einen Fehler auslöst.
Zum Beispiel:
abstraktKlasseArtikelZustandimplementiertArtikelSchnittstelle{
pitch(): ArticleState {
WurfneuFehler("Ungültiger Vorgang: Aufgabe kann nicht ausgeführt werden In aktuellen Zustand");
}Entwurf(): ArtikelZustand {
WurfneuFehler("Ungültiger Vorgang: Aufgabe kann nicht ausgeführt werden In aktuellen Zustand");
}edit(): ArtikelZustand {
WurfneuFehler("Ungültiger Vorgang: Aufgabe kann nicht ausgeführt werden In aktuellen Zustand");
}
veröffentlichen(): ArtikelZustand {
WurfneuFehler("Ungültiger Vorgang: Aufgabe kann nicht ausgeführt werden In aktuellen Zustand");
}
}
In der obigen Basisklasse wirft jede Methode einen Fehler. Jetzt müssen Sie jede Methode überschreiben, indem Sie bestimmte Klassen dafür erstellen erweitert die Basisklasse für jeden Zustand. Jede spezifische Klasse enthält eine zustandsspezifische Logik.
Jede Anwendung hat einen Ruhezustand, der die Anwendung initialisiert. Der Ruhezustand für diese Anwendung setzt die Anwendung auf den Entwurf Zustand.
Zum Beispiel:
KlasseAusstehender EntwurfsstatuserweitertArtikelZustand{
pitch(): ArticleState {
zurückkehrenneu Entwurfsstatus ();
}
}
Der Tonhöhe -Methode in der obigen Klasse initialisiert die Anwendung, indem sie den aktuellen Status auf setzt Entwurfsstatus.
Überschreiben Sie als Nächstes die restlichen Methoden wie folgt:
KlasseEntwurfsstatuserweitertArtikelZustand{
Entwurf(): ArtikelZustand {
zurückkehrenneu Bearbeitungszustand ();
}
}
Dieser Code überschreibt die Entwurf -Methode und gibt eine Instanz von zurück Bearbeitungszustand.
KlasseBearbeitungszustanderweitertArtikelZustand{
edit(): ArtikelZustand {
zurückkehrenneu PublishedState();
}
}
Der obige Codeblock überschreibt die bearbeiten -Methode und gibt eine Instanz von zurück PublishedState.
KlassePublishedStateerweitertArtikelZustand{
veröffentlichen(): ArtikelZustand {
zurückkehrenneu PendingDraftState();
}
}
Der obige Codeblock überschreibt die veröffentlichen -Methode und versetzt die Anwendung wieder in ihren Ruhezustand, Ausstehender Entwurfsstatus.
Anschließend müssen Sie der Anwendung erlauben, ihren Status intern zu ändern, indem Sie über eine private Variable auf den aktuellen Status verweisen. Sie können dies tun, indem Sie den Ruhezustand in Ihrer Anwendungsklasse initialisieren und den Wert in einer privaten Variablen speichern:
Privatgelände Zustand: ArticleState = neu PendingDraftState();
Als nächstes aktualisieren Sie die aktuellenZustand anzeigen Methode zum Drucken des aktuellen Statuswerts:
PrivatgeländeaktuellenZustand anzeigen(): Leere{
Konsole.Protokoll(Das.Zustand);
}
Der aktuellenZustand anzeigen -Methode protokolliert den aktuellen Status der Anwendung in der Konsole.
Weisen Sie schließlich die private Variable der aktuellen Zustandsinstanz in jeder Ihrer Anwendungsmethoden neu zu.
Aktualisieren Sie beispielsweise Ihre Anwendungen Tonhöhe Methode zum folgenden Codeblock:
öffentlichTonhöhe(): Leere{
Das.zustand = Das.state.pitch();
Das.showCurrentState();
}
Im obigen Codeblock ist die Tonhöhe -Methode ändert den Zustand vom aktuellen Zustand in den Tonhöhenzustand.
In ähnlicher Weise ändern alle anderen Methoden den Status vom aktuellen Anwendungsstatus in ihre jeweiligen Status.
Aktualisieren Sie Ihre Anwendungsmethoden auf die folgenden Codeblöcke:
Der Entwurf Methode:
öffentlichEntwurf(): Leere{
Das.zustand = Das.state.draft();
Das.showCurrentState();
}
Der bearbeiten Methode:
öffentlichbearbeiten(): Leere{
Das.zustand = Das.state.edit();
Das.showCurrentState();
}
Und das veröffentlichen Methode:
öffentlichveröffentlichen(): Leere{
Das.zustand = Das.state.publish();
Das.showCurrentState();
}
Verwenden der fertigen Anwendung
Ihre fertige Anwendungsklasse sollte dem folgenden Codeblock ähneln:
// Anwendung
KlasseArtikelimplementiertArtikelSchnittstelle{
Privatgelände Zustand: ArticleState = neu PendingDraftState();Konstrukteur() {
Das.showCurrentState();
}PrivatgeländeaktuellenZustand anzeigen(): Leere{
Konsole.Protokoll(Das.Zustand);
}öffentlichTonhöhe(): Leere{
Das.zustand = Das.state.pitch();
Das.showCurrentState();
}öffentlichEntwurf(): Leere{
Das.zustand = Das.state.draft();
Das.showCurrentState();
}öffentlichbearbeiten(): Leere{
Das.zustand = Das.state.edit();
Das.showCurrentState();
}
öffentlichveröffentlichen(): Leere{
Das.zustand = Das.state.publish();
Das.showCurrentState();
}
}
Sie können die Zustandsübergänge testen, indem Sie die Methoden in der richtigen Reihenfolge aufrufen. Zum Beispiel:
konst Dokumente = neu Artikel(); // PendingDraftState: {}
docs.pitch(); // Entwurfsstatus: {}
docs.draft(); // Bearbeitungsstatus: {}
docs.edit(); // Veröffentlichter Zustand: {}
docs.publish(); // PendingDraftState: {}
Der obige Codeblock funktioniert, weil die Status der Anwendung entsprechend geändert wurden.
Wenn Sie versuchen, den Status auf eine nicht zulässige Weise zu ändern, z. B. vom Pitch-Status in den Bearbeitungsstatus, gibt die Anwendung einen Fehler aus:
konst Dokumente = neu Artikel(); // PendingDraftState: {}
docs.pitch() // Entwurfsstatus: {}
docs.edit() // Ungültige Operation: Aufgabe kann im aktuellen Zustand nicht ausgeführt werden
Sie sollten dieses Muster nur verwenden, wenn:
- Sie erstellen ein Objekt, das sich je nach aktuellem Zustand unterschiedlich verhält.
- Das Objekt hat viele Zustände.
- Das zustandsspezifische Verhalten ändert sich häufig.
Vorteile und Kompromisse des Zustandsmusters
Dieses Muster eliminiert sperrige bedingte Anweisungen und behält die Prinzipien der Einzelverantwortung und des Offenen/Geschlossenen bei. Aber es kann übertrieben sein, wenn die Anwendung wenige Zustände hat oder ihre Zustände nicht besonders dynamisch sind.