Un model de design este un șablon care rezolvă o problemă care se repetă frecvent în proiectarea software.
Modelul de stare este un model comportamental care permite unui obiect să-și modifice comportamentul atunci când starea sa internă se schimbă.
Aici veți învăța cum să utilizați modelul de stare în TypeScript.
Ce este modelul de stat?
Modelul de proiectare a stării este strâns legat de o mașină cu stări finite, care descrie un program care există într-un finit număr de stări la un moment dat și se comportă diferit în cadrul fiecărei stări.
Există reguli limitate, predeterminate – tranziții – care guvernează celelalte state la care fiecare stat poate trece.
Pentru context, într-un magazin online, dacă comanda de cumpărături a unui client a fost „livrată”, aceasta nu poate fi „anulată” deoarece a fost deja „livrată”. „Livrat” și „Anulat” sunt stări finite ale comenzii, iar comanda se va comporta diferit în funcție de starea sa.
Modelul de stat creează o clasă pentru fiecare stare posibilă, cu comportamentul specific de stat cuprins în fiecare clasă.
Un exemplu de aplicație bazată pe stat
De exemplu, să presupunem că creați o aplicație care urmărește stările unui articol pentru o companie de editură. Un articol poate fi fie în așteptarea aprobării, fie redactat de un scriitor, editat de un editor sau publicat. Acestea sunt stările finite ale unui articol care urmează să fie publicat; în cadrul fiecărei stări unice, articolul se comportă diferit.
Puteți vizualiza diferitele stări și tranziții ale aplicației articol cu diagrama de stare de mai jos:
Implementând acest scenariu în cod, mai întâi ar trebui să declarați o interfață pentru articol:
interfataInterfața articolului{
pas(): gol;
proiect(): gol;
Editați | ×(): gol;
publica(): gol;
}
Această interfață va avea toate stările posibile ale aplicației.
Apoi, creați o aplicație care implementează toate metodele de interfață:
// Aplicație
clasăArticolunelteInterfața articolului{
constructor() {
acest.showCurrentState();
}privatshowCurrentState(): gol{
//...
}publicpas(): gol{
//...
}publicproiect(): gol{
//...
}publicEditați | ×(): gol{
//...
}
publicpublica(): gol{
//...
}
}
Privat showCurrentState metoda este o metodă de utilitate. Acest tutorial îl folosește pentru a arăta ce se întâmplă în fiecare stare. Nu este o parte obligatorie a modelului de stat.
Gestionarea tranzițiilor de stat
În continuare, va trebui să gestionați tranzițiile de stare. Gestionarea tranziției de stare în clasa dvs. de aplicație ar necesita multe declarații condiționale. Acest lucru ar duce la un cod repetitiv, care este mai greu de citit și de întreținut. Pentru a rezolva această problemă, puteți delega logica de tranziție pentru fiecare stare propriei sale clase.
Înainte de a scrie fiecare clasă de stare, ar trebui să creați o clasă de bază abstractă pentru a vă asigura că orice metodă numită într-o stare nevalidă aruncă o eroare.
De exemplu:
abstractclasăArticolulStateunelteInterfața articolului{
pitch(): ArticleState {
aruncanouEroare("Operațiune nevalidă: nu se poate executa sarcina în starea curenta");
}draft(): ArticleState {
aruncanouEroare("Operațiune nevalidă: nu se poate executa sarcina în starea curenta");
}edit(): ArticleState {
aruncanouEroare("Operațiune nevalidă: nu se poate executa sarcina în starea curenta");
}
publish(): ArticleState {
aruncanouEroare("Operațiune nevalidă: nu se poate executa sarcina în starea curenta");
}
}
În clasa de bază de mai sus, fiecare metodă aruncă o eroare. Acum, trebuie să suprascrieți fiecare metodă prin crearea unor clase specifice care se extinde clasa de bază pentru fiecare stat. Fiecare clasă specifică va conține logica specifică stării.
Fiecare aplicație are o stare inactivă, care inițializează aplicația. Starea inactiv pentru această aplicație va seta aplicația la proiect stat.
De exemplu:
clasăPendingDraftStatese extindeArticolulState{
pitch(): ArticleState {
întoarcerenou DraftState();
}
}
The pas metoda din clasa de mai sus inițializează aplicația setând starea curentă la DraftState.
Apoi, suprascrieți restul metodelor astfel:
clasăDraftStatese extindeArticolulState{
draft(): ArticleState {
întoarcerenou EditingState();
}
}
Acest cod suprascrie proiect metoda și returnează o instanță a EditingState.
clasăEditingStatese extindeArticolulState{
edit(): ArticleState {
întoarcerenou PublishedState();
}
}
Blocul de cod de mai sus suprascrie Editați | × metoda și returnează o instanță a PublishedState.
clasăPublishedStatese extindeArticolulState{
publish(): ArticleState {
întoarcerenou PendingDraftState();
}
}
Blocul de cod de mai sus suprascrie publica metoda și pune aplicația înapoi în starea inactivă, PendingDraftState.
Apoi, trebuie să permiteți aplicației să-și schimbe starea intern, prin referirea stării curente printr-o variabilă privată. Puteți face acest lucru prin inițializarea stării inactiv în interiorul clasei de aplicație și stocarea valorii într-o variabilă privată:
privat stat: ArticleState = nou PendingDraftState();
Apoi, actualizați showCurrentState metoda de imprimare a valorii stării curente:
privatshowCurrentState(): gol{
consolă.Buturuga(acest.stat);
}
The showCurrentState metoda înregistrează starea curentă a aplicației pe consolă.
În cele din urmă, reatribuiți variabila privată instanței de stare curentă în fiecare dintre metodele aplicației dvs.
De exemplu, actualizați aplicațiile pas metoda la blocul de cod de mai jos:
publicpas(): gol{
acest.state = acest.state.pitch();
acest.showCurrentState();
}
În blocul de cod de mai sus, pas metoda schimbă starea de la starea curentă la starea de înălțime.
În mod similar, toate celelalte metode vor schimba starea din starea curentă a aplicației în stările respective.
Actualizați metodele de aplicare la blocurile de cod de mai jos:
The proiect metodă:
publicproiect(): gol{
acest.state = acest.state.draft();
acest.showCurrentState();
}
The Editați | × metodă:
publicEditați | ×(): gol{
acest.state = acest.state.edit();
acest.showCurrentState();
}
Si publica metodă:
publicpublica(): gol{
acest.state = acest.state.publish();
acest.showCurrentState();
}
Utilizarea aplicației finalizate
Clasa finală de aplicație ar trebui să fie similară cu blocul de cod de mai jos:
// Aplicație
clasăArticolunelteInterfața articolului{
privat stat: ArticleState = nou PendingDraftState();constructor() {
acest.showCurrentState();
}privatshowCurrentState(): gol{
consolă.Buturuga(acest.stat);
}publicpas(): gol{
acest.state = acest.state.pitch();
acest.showCurrentState();
}publicproiect(): gol{
acest.state = acest.state.draft();
acest.showCurrentState();
}publicEditați | ×(): gol{
acest.state = acest.state.edit();
acest.showCurrentState();
}
publicpublica(): gol{
acest.state = acest.state.publish();
acest.showCurrentState();
}
}
Puteți testa tranzițiile de stare apelând metodele în secvența corectă. De exemplu:
const documente = nou Articol(); // PendingDraftState: {}
docs.pitch(); // DraftState: {}
docs.draft(); // EditingState: {}
docs.edit(); // PublishedState: {}
docs.publish(); // PendingDraftState: {}
Blocul de cod de mai sus funcționează deoarece stările aplicației au trecut în mod corespunzător.
Dacă încercați să schimbați starea într-un mod care nu este permis, de exemplu, de la starea de pitch la starea de editare, aplicația va arunca o eroare:
const documente = nou Articol(); // PendingDraftState: {}
docs.pitch() // DraftState: {}
docs.edit() // Operație nevalidă: Nu se poate executa sarcina în starea curentă
Ar trebui să utilizați acest model numai atunci când:
- Creați un obiect care se comportă diferit în funcție de starea sa actuală.
- Obiectul are multe stări.
- Comportamentul specific de stat se schimbă frecvent.
Avantajele și compromisurile modelului de stat
Acest model elimină declarațiile condiționate voluminoase și menține responsabilitatea unică și principiile deschise/închise. Dar poate fi exagerat dacă aplicația are puține stări sau stările sale nu sunt deosebit de dinamice.