Un modello di progettazione è un modello che risolve un problema ricorrente nella progettazione del software.

Il modello di stato è un modello comportamentale che consente a un oggetto di modificare il proprio comportamento quando il suo stato interno cambia.

Qui imparerai come utilizzare il modello di stato in TypeScript.

Qual è il modello di stato?

Il modello di progettazione dello stato è strettamente correlato a una macchina a stati finiti, che descrive un programma che esiste in a finito numero di stati in un dato momento e si comporta in modo diverso all'interno di ciascuno stato.

Esistono regole limitate e predeterminate, le transizioni, che governano gli altri stati a cui ogni stato può passare.

Per il contesto, in un negozio online, se l'ordine di acquisto di un cliente è stato "consegnato", non può essere "annullato" perché è già stato "consegnato". "Consegnato" e "Annullato" sono stati finiti dell'ordine e l'ordine si comporterà in modo diverso in base al suo stato.

Il modello di stato

instagram viewer
crea una classe per ogni stato possibile, con comportamento specifico dello stato contenuto in ogni classe.

Un esempio di applicazione basata sullo stato

Si supponga, ad esempio, di creare un'applicazione che tenga traccia degli stati di un articolo per una casa editrice. Un articolo può essere in attesa di approvazione, redatto da uno scrittore, modificato da un editore o pubblicato. Questi sono gli stati finiti di un articolo da pubblicare; all'interno di ogni stato univoco, l'articolo si comporta in modo diverso.

È possibile visualizzare i diversi stati e le transizioni dell'applicazione dell'articolo con il diagramma di stato seguente:

Implementando questo scenario nel codice, dovresti prima dichiarare un'interfaccia per l'articolo:

interfacciaInterfaccia dell'articolo{
pece(): vuoto;
bozza(): vuoto;
modificare(): vuoto;
pubblicare(): vuoto;
}

Questa interfaccia avrà tutti i possibili stati dell'applicazione.

Successivamente, crea un'applicazione che implementa tutti i metodi dell'interfaccia:

// Applicazione
classeArticoloimplementaInterfaccia dell'articolo{
costruttore() {
Questo.showCurrentState();
}

privatoshowCurrentState(): vuoto{
//...
}

pubblicopece(): vuoto{
//...
}

pubblicobozza(): vuoto{
//...
}

pubblicomodificare(): vuoto{
//...
}

pubblicopubblicare(): vuoto{
//...
}
}

Il privato showCurrentState metodo è un metodo di utilità. Questo tutorial lo usa per mostrare cosa succede in ogni stato. Non è una parte obbligatoria del modello di stato.

Gestione delle transizioni di stato

Successivamente, dovrai gestire le transizioni di stato. La gestione della transizione di stato nella classe dell'applicazione richiederebbe molti affermazioni condizionali. Ciò comporterebbe un codice ripetitivo più difficile da leggere e mantenere. Per risolvere questo problema, puoi delegare la logica di transizione per ogni stato alla propria classe.

Prima di scrivere ogni classe di stato, è necessario creare una classe di base astratta per garantire che qualsiasi metodo chiamato in uno stato non valido generi un errore.

Per esempio:

astrattoclasseArticoloStatoimplementaInterfaccia dell'articolo{
pitch(): ArticleState {
gettarenuovoErrore("Operazione non valida: impossibile eseguire l'attività In stato attuale");
}

bozza(): ArticleState {
gettarenuovoErrore("Operazione non valida: impossibile eseguire l'attività In stato attuale");
}

edit(): StatoArticolo {
gettarenuovoErrore("Operazione non valida: impossibile eseguire l'attività In stato attuale");
}

pubblicare(): ArticleState {
gettarenuovoErrore("Operazione non valida: impossibile eseguire l'attività In stato attuale");
}
}

Nella classe base sopra, ogni metodo genera un errore. Ora devi sovrascrivere ogni metodo creando classi specifiche che estende la classe base per ogni stato. Ogni classe specifica conterrà la logica specifica dello stato.

Ogni applicazione ha uno stato inattivo, che inizializza l'applicazione. Lo stato inattivo per questa applicazione imposterà l'applicazione su bozza stato.

Per esempio:

classeStato bozza in sospesoestendeArticoloStato{
pitch(): ArticleState {
ritornonuovo BozzaStato();
}
}

IL pece Il metodo nella classe precedente inizializza l'applicazione impostando lo stato corrente su BozzaStato.

Quindi, sovrascrivi il resto dei metodi in questo modo:

classeBozzaStatoestendeArticoloStato{
bozza(): ArticleState {
ritornonuovo EditingState();
}
}

Questo codice sovrascrive il bozza metodo e restituisce un'istanza di Stato di modifica.

classeStato di modificaestendeArticoloStato{
edit(): StatoArticolo {
ritornonuovo Statopubblicato();
}
}

Il blocco di codice sopra sovrascrive il file modificare metodo e restituisce un'istanza di Stato pubblicato.

classeStato pubblicatoestendeArticoloStato{
pubblicare(): ArticleState {
ritornonuovo PendingDraftState();
}
}

Il blocco di codice sopra sovrascrive il file pubblicare metodo e riporta l'applicazione nel suo stato inattivo, Stato bozza in sospeso.

Quindi, è necessario consentire all'applicazione di modificare il proprio stato internamente facendo riferimento allo stato corrente tramite una variabile privata. Puoi farlo inizializzando lo stato inattivo all'interno della classe dell'applicazione e memorizzando il valore in una variabile privata:

privato stato: ArticleState = nuovo PendingDraftState();

Successivamente, aggiorna il file showCurrentState metodo per stampare il valore dello stato corrente:

privatoshowCurrentState(): vuoto{
consolare.tronco d'albero(Questo.stato);
}

IL showCurrentState Il metodo registra lo stato corrente dell'applicazione nella console.

Infine, riassegna la variabile privata all'istanza di stato corrente in ciascuno dei metodi dell'applicazione.

Ad esempio, aggiorna le tue applicazioni pece metodo al blocco di codice seguente:

pubblicopece(): vuoto{
Questo.stato = Questo.state.pitch();
Questo.showCurrentState();
}

Nel blocco di codice sopra, il pece Il metodo cambia lo stato dallo stato corrente allo stato del tono.

Allo stesso modo, tutti gli altri metodi cambieranno lo stato dall'attuale stato dell'applicazione ai rispettivi stati.

Aggiorna i tuoi metodi di applicazione ai blocchi di codice seguenti:

IL bozza metodo:

pubblicobozza(): vuoto{
Questo.stato = Questo.stato.bozza();
Questo.showCurrentState();
}

IL modificare metodo:

pubblicomodificare(): vuoto{
Questo.stato = Questo.state.edit();
Questo.showCurrentState();
}

E il pubblicare metodo:

pubblicopubblicare(): vuoto{
Questo.stato = Questo.state.publish();
Questo.showCurrentState();
}

Utilizzando l'applicazione finita

La tua classe di applicazione finita dovrebbe essere simile al blocco di codice qui sotto:

// Applicazione
classeArticoloimplementaInterfaccia dell'articolo{
privato stato: ArticleState = nuovo PendingDraftState();

costruttore() {
Questo.showCurrentState();
}

privatoshowCurrentState(): vuoto{
consolare.tronco d'albero(Questo.stato);
}

pubblicopece(): vuoto{
Questo.stato = Questo.state.pitch();
Questo.showCurrentState();
}

pubblicobozza(): vuoto{
Questo.stato = Questo.stato.bozza();
Questo.showCurrentState();
}

pubblicomodificare(): vuoto{
Questo.stato = Questo.state.edit();
Questo.showCurrentState();
}

pubblicopubblicare(): vuoto{
Questo.stato = Questo.state.publish();
Questo.showCurrentState();
}
}

È possibile testare le transizioni di stato chiamando i metodi nella sequenza corretta. Per esempio:

cost documenti = nuovo Articolo(); // PendingDraftState: {}

docs.pitch(); // Stato bozza: {}
docs.bozza(); // Stato di modifica: {}
docs.edit(); // Stato pubblicato: {}
docs.publish(); // PendingDraftState: {}

Il blocco di codice sopra funziona perché gli stati dell'applicazione sono passati in modo appropriato.

Se provi a modificare lo stato in un modo non consentito, ad esempio dallo stato pitch allo stato edit, l'applicazione genererà un errore:

cost documenti = nuovo Articolo(); // PendingDraftState: {}
docs.pitch() // Stato bozza: {}
docs.edit() // Operazione non valida: impossibile eseguire l'attività nello stato corrente

Dovresti utilizzare questo modello solo quando:

  • Stai creando un oggetto che si comporta in modo diverso a seconda del suo stato attuale.
  • L'oggetto ha molti stati.
  • Il comportamento specifico dello stato cambia frequentemente.

Vantaggi e compromessi del modello di stato

Questo modello elimina dichiarazioni condizionali ingombranti e mantiene i principi di responsabilità singola e aperto/chiuso. Ma può essere eccessivo se l'applicazione ha pochi stati o i suoi stati non sono particolarmente dinamici.