Wzorzec projektowy to szablon, który rozwiązuje często powtarzający się problem w projektowaniu oprogramowania.

Wzorzec stanu to wzorzec behawioralny, który pozwala obiektowi zmieniać swoje zachowanie, gdy zmienia się jego stan wewnętrzny.

Tutaj dowiesz się, jak używać wzorca stanu w TypeScript.

Co to jest wzorzec stanu?

Wzorzec projektowy stanu jest ściśle powiązany z maszyną skończoną, która opisuje program istniejący w pliku a skończone liczbę stanów w danym momencie i zachowuje się inaczej w każdym stanie.

Istnieją ograniczone, z góry określone zasady — przejścia — które rządzą innymi stanami, do których każdy stan może się przełączyć.

Dla kontekstu, w sklepie internetowym, jeśli zamówienie klienta zostało „dostarczone”, nie można go „anulować”, ponieważ zostało już „dostarczone”. „Dostarczone” i „Anulowane” to skończone stany zamówienia, a zamówienie będzie zachowywać się inaczej w zależności od jego stanu.

Wzór stanu tworzy klasę dla każdego możliwego stanu, z zachowaniem specyficznym dla stanu zawartym w każdej klasie.

instagram viewer

Przykładowa aplikacja oparta na stanie

Załóżmy na przykład, że tworzysz aplikację, która śledzi stany artykułu dla firmy wydawniczej. Artykuł może oczekiwać na zatwierdzenie, być przygotowany przez pisarza, zredagowany przez redaktora lub opublikowany. Są to skończone stany artykułu, który ma zostać opublikowany; w każdym unikalnym stanie artykuł zachowuje się inaczej.

Możesz wizualizować różne stany i przejścia aplikacji artykułu za pomocą poniższego diagramu stanów:

Implementując ten scenariusz w kodzie, musisz najpierw zadeklarować interfejs dla artykułu:

interfejsArtykułInterfejs{
poziom(): próżnia;
projekt(): próżnia;
edytować(): próżnia;
publikować(): próżnia;
}

Ten interfejs będzie miał wszystkie możliwe stany aplikacji.

Następnie utwórz aplikację, która implementuje wszystkie metody interfejsu:

// Aplikacja
klasaArtykułprzyboryArtykułInterfejs{
konstruktor() {
Ten.showCurrentState();
}

prywatnypokaż bieżący stan(): próżnia{
//...
}

publicznypoziom(): próżnia{
//...
}

publicznyprojekt(): próżnia{
//...
}

publicznyedytować(): próżnia{
//...
}

publicznypublikować(): próżnia{
//...
}
}

Prywatny pokaż bieżący stan metoda jest metodą użytkową. Ten samouczek używa go, aby pokazać, co dzieje się w każdym stanie. Nie jest to wymagana część wzorca stanu.

Obsługa przejść między stanami

Następnie musisz obsłużyć przejścia między stanami. Obsługa przejścia stanu w klasie aplikacji wymagałaby wielu Instrukcje warunkowe. Spowodowałoby to powstanie powtarzalnego kodu, który jest trudniejszy do odczytania i utrzymania. Aby rozwiązać ten problem, możesz delegować logikę przejścia dla każdego stanu do jego własnej klasy.

Przed napisaniem każdej klasy stanu należy utworzyć abstrakcyjną klasę bazową, aby mieć pewność, że każda metoda wywołana w nieprawidłowym stanie zgłosi błąd.

Na przykład:

abstrakcyjnyklasaStan artykułuprzyboryArtykułInterfejs{
pitch(): Stan Artykułu {
rzucićnowyBłąd(„Nieprawidłowa operacja: nie można wykonać zadania W stan aktulany");
}

wersja robocza(): stan artykułu {
rzucićnowyBłąd(„Nieprawidłowa operacja: nie można wykonać zadania W stan aktulany");
}

edit(): Stan Artykułu {
rzucićnowyBłąd(„Nieprawidłowa operacja: nie można wykonać zadania W stan aktulany");
}

opublikuj(): stan artykułu {
rzucićnowyBłąd(„Nieprawidłowa operacja: nie można wykonać zadania W stan aktulany");
}
}

W powyższej klasie bazowej każda metoda zgłasza błąd. Teraz musisz zastąpić każdą metodę, tworząc określone klasy rozciąga się klasa bazowa dla każdego stanu. Każda konkretna klasa będzie zawierać logikę specyficzną dla stanu.

Każda aplikacja ma stan bezczynności, który inicjuje aplikację. Stan bezczynności tej aplikacji ustawi aplikację na projekt państwo.

Na przykład:

klasaOczekujący stan roboczyrozciąga sięStan artykułu{
pitch(): Stan Artykułu {
powrótnowy Stan Szkicowy();
}
}

The poziom Metoda w powyższej klasie inicjuje aplikację, ustawiając bieżący stan na Stan szkicu.

Następnie zastąp pozostałe metody w następujący sposób:

klasaStan szkicurozciąga sięStan artykułu{
wersja robocza(): stan artykułu {
powrótnowy stan edycji();
}
}

Ten kod zastępuje projekt metoda i zwraca instancję metody Stan edycji.

klasaStan edycjirozciąga sięStan artykułu{
edit(): Stan Artykułu {
powrótnowy stan publikacji();
}
}

Powyższy blok kodu zastępuje edytować metoda i zwraca instancję stan publikacji.

klasastan publikacjirozciąga sięStan artykułu{
opublikuj(): stan artykułu {
powrótnowy Oczekujący stan szkicu();
}
}

Powyższy blok kodu zastępuje publikować metody i przywraca aplikację do stanu bezczynności, Oczekujący stan roboczy.

Następnie musisz zezwolić aplikacji na wewnętrzną zmianę stanu poprzez odwołanie się do bieżącego stanu za pomocą zmiennej prywatnej. Możesz to zrobić, inicjując stan bezczynności w swojej klasie aplikacji i przechowując wartość w zmiennej prywatnej:

prywatny stan: stan artykułu = nowy Oczekujący stan szkicu();

Następnie zaktualizuj plik pokaż bieżący stan metoda drukowania aktualnej wartości stanu:

prywatnypokaż bieżący stan(): próżnia{
konsola.dziennik(Ten.państwo);
}

The pokaż bieżący stan metoda rejestruje bieżący stan aplikacji w konsoli.

Na koniec ponownie przypisz zmienną prywatną do instancji bieżącego stanu w każdej metodzie aplikacji.

Na przykład zaktualizuj swoje aplikacje poziom metoda do bloku kodu poniżej:

publicznypoziom(): próżnia{
Tenstan = Ten.stan.wysokość();
Ten.showCurrentState();
}

W powyższym bloku kodu plik poziom Metoda zmienia stan ze stanu bieżącego na stan skoku.

Podobnie wszystkie inne metody zmienią stan z bieżącego stanu aplikacji na odpowiednie stany.

Zaktualizuj swoje metody aplikacji do poniższych bloków kodu:

The projekt metoda:

publicznyprojekt(): próżnia{
Tenstan = Ten.stan.draft();
Ten.showCurrentState();
}

The edytować metoda:

publicznyedytować(): próżnia{
Tenstan = Tenstan.edytuj();
Ten.showCurrentState();
}

i publikować metoda:

publicznypublikować(): próżnia{
Tenstan = Ten.state.publish();
Ten.showCurrentState();
}

Korzystanie z gotowej aplikacji

Gotowa klasa aplikacji powinna być podobna do poniższego bloku kodu:

// Aplikacja
klasaArtykułprzyboryArtykułInterfejs{
prywatny stan: stan artykułu = nowy Oczekujący stan szkicu();

konstruktor() {
Ten.showCurrentState();
}

prywatnypokaż bieżący stan(): próżnia{
konsola.dziennik(Ten.państwo);
}

publicznypoziom(): próżnia{
Tenstan = Ten.stan.wysokość();
Ten.showCurrentState();
}

publicznyprojekt(): próżnia{
Tenstan = Ten.stan.draft();
Ten.showCurrentState();
}

publicznyedytować(): próżnia{
Tenstan = Tenstan.edytuj();
Ten.showCurrentState();
}

publicznypublikować(): próżnia{
Tenstan = Ten.state.publish();
Ten.showCurrentState();
}
}

Możesz przetestować przejścia między stanami, wywołując metody w odpowiedniej kolejności. Na przykład:

konst dokumenty = nowy Artykuł(); // Oczekujący stan szkicu: {}

docs.pitch(); // Stan szkicu: {}
docs.draft(); // stan edycji: {}
dokumenty.edytuj(); // stan publikacji: {}
dokumenty.opublikuj(); // Oczekujący stan szkicu: {}

Powyższy blok kodu działa, ponieważ stany aplikacji przeszły odpowiednio.

Jeśli spróbujesz zmienić stan w sposób niedozwolony, np. ze stanu skoku do stanu edycji, aplikacja zgłosi błąd:

konst dokumenty = nowy Artykuł(); // Oczekujący stan szkicu: {}
docs.pitch() // Stan szkicu: {}
dokumenty.edytuj() // Nieprawidłowa operacja: Nie można wykonać zadania w bieżącym stanie

Powinieneś używać tego wzorca tylko wtedy, gdy:

  • Tworzysz obiekt, który zachowuje się różnie w zależności od swojego aktualnego stanu.
  • Obiekt ma wiele stanów.
  • Zachowanie specyficzne dla stanu często się zmienia.

Zalety i kompromisy wzorca stanu

Ten wzorzec eliminuje obszerne instrukcje warunkowe i zachowuje pojedynczą odpowiedzialność oraz zasady otwarte/zamknięte. Ale może to być przesada, jeśli aplikacja ma niewiele stanów lub jej stany nie są szczególnie dynamiczne.