Et designmønster er en skabelon, der løser et almindeligt tilbagevendende problem i softwaredesign.
Tilstandsmønsteret er et adfærdsmønster, der lader et objekt ændre sin adfærd, når dets indre tilstand ændrer sig.
Her lærer du, hvordan du bruger tilstandsmønsteret i TypeScript.
Hvad er statens mønster?
Tilstandsdesignmønsteret er tæt forbundet med en finite-state maskine, som beskriver et program, der eksisterer i en begrænset antal stater på et givet tidspunkt og opfører sig forskelligt inden for hver stat.
Der er begrænsede, forudbestemte regler - overgange - der styrer de andre stater, som hver stat kan skifte til.
For kontekst, i en onlinebutik, hvis en kundes indkøbsordre er blevet "leveret", kan den ikke "annulleres", fordi den allerede er blevet "leveret". "Leveret" og "Annulleret" er endelige tilstande af ordren, og ordren vil opføre sig anderledes baseret på dens tilstand.
Statsmønsteret opretter en klasse for hver mulig tilstand, med tilstandsspecifik adfærd indeholdt i hver klasse.
Et eksempel på statsbaseret applikation
Antag for eksempel, at du opretter en applikation, der sporer tilstanden for en artikel for et forlag. En artikel kan enten afvente godkendelse, udarbejdet af en skribent, redigeret af en redaktør eller offentliggjort. Disse er de endelige tilstande af en artikel, der skal publiceres; inden for hver unik tilstand opfører artiklen sig forskelligt.
Du kan visualisere de forskellige tilstande og overgange i artikelapplikationen med tilstandsdiagrammet nedenfor:
Ved at implementere dette scenarie i kode skal du først erklære en grænseflade til artiklen:
interfaceArtikelgrænseflade{
tonehøjde(): ugyldig;
udkast(): ugyldig;
redigere(): ugyldig;
offentliggøre(): ugyldig;
}
Denne grænseflade vil have alle applikationens mulige tilstande.
Derefter skal du oprette en applikation, der implementerer alle grænseflademetoderne:
// Ansøgning
klasseArtikelredskaberArtikelgrænseflade{
konstruktør() {
det her.showCurrentState();
}privatshowCurrentState(): ugyldig{
//...
}offentligtonehøjde(): ugyldig{
//...
}offentligudkast(): ugyldig{
//...
}offentligredigere(): ugyldig{
//...
}
offentligoffentliggøre(): ugyldig{
//...
}
}
Det private showCurrentState metode er en brugsmetode. Denne tutorial bruger den til at vise, hvad der sker i hver stat. Det er ikke en påkrævet del af statsmønsteret.
Håndtering af statsovergange
Dernæst skal du håndtere tilstandsovergangene. Håndtering af tilstandsovergangen i din applikationsklasse ville kræve mange betingede erklæringer. Dette ville resultere i gentagen kode, der er sværere at læse og vedligeholde. For at løse dette problem kan du delegere overgangslogikken for hver stat til sin egen klasse.
Før du skriver hver tilstandsklasse, bør du oprette en abstrakt basisklasse for at sikre, at enhver metode, der kaldes i en ugyldig tilstand, kaster en fejl.
For eksempel:
abstraktklasseArtikelStateredskaberArtikelgrænseflade{
pitch(): ArticleState {
kastenyFejl("Ugyldig handling: Kan ikke udføre opgaven i nuværende tilstand");
}udkast(): ArticleState {
kastenyFejl("Ugyldig handling: Kan ikke udføre opgaven i nuværende tilstand");
}edit(): ArticleState {
kastenyFejl("Ugyldig handling: Kan ikke udføre opgaven i nuværende tilstand");
}
publicer(): ArticleState {
kastenyFejl("Ugyldig handling: Kan ikke udføre opgaven i nuværende tilstand");
}
}
I basisklassen ovenfor giver hver metode en fejl. Nu skal du tilsidesætte hver metode ved at oprette specifikke klasser, der strækker sig basisklassen for hver stat. Hver specifik klasse vil indeholde tilstandsspecifik logik.
Hver applikation har en inaktiv tilstand, som initialiserer applikationen. Inaktiv tilstand for denne applikation vil indstille applikationen til udkast stat.
For eksempel:
klasseAfventendeDraftStatestrækker sigArtikelState{
pitch(): ArticleState {
Vend tilbageny DraftState();
}
}
Det tonehøjde metode i klassen ovenfor initialiserer applikationen ved at indstille den aktuelle tilstand til DraftState.
Tilsidesæt derefter resten af metoderne som sådan:
klasseDraftStatestrækker sigArtikelState{
udkast(): ArticleState {
Vend tilbageny EditingState();
}
}
Denne kode tilsidesætter udkast metode og returnerer en forekomst af Redigeringstilstand.
klasseRedigeringstilstandstrækker sigArtikelState{
edit(): ArticleState {
Vend tilbageny PublishedState();
}
}
Kodeblokken ovenfor tilsidesætter redigere metode og returnerer en forekomst af UdgivetStat.
klasseUdgivetStatstrækker sigArtikelState{
publicer(): ArticleState {
Vend tilbageny PendingDraftState();
}
}
Kodeblokken ovenfor tilsidesætter offentliggøre metode og sætter applikationen tilbage i sin inaktive tilstand, AfventendeDraftState.
Derefter skal du tillade applikationen at ændre sin tilstand internt ved at referere til den aktuelle tilstand gennem en privat variabel. Du kan gøre dette ved at initialisere den inaktive tilstand i din applikationsklasse og gemme værdien til en privat variabel:
privat tilstand: ArticleState = ny PendingDraftState();
Dernæst skal du opdatere showCurrentState metode til at udskrive den aktuelle tilstandsværdi:
privatshowCurrentState(): ugyldig{
konsol.log(det her.stat);
}
Det showCurrentState metoden logger den aktuelle tilstand af applikationen til konsollen.
Til sidst skal du gentildele den private variabel til den aktuelle tilstandsinstans i hver af din applikations metoder.
Opdater f.eks. dine applikationer tonehøjde metode til kodeblokken nedenfor:
offentligtonehøjde(): ugyldig{
det her.state = det her.state.pitch();
det her.showCurrentState();
}
I kodeblokken ovenfor er tonehøjde metode ændrer tilstanden fra den aktuelle tilstand til pitchtilstanden.
På samme måde vil alle de andre metoder ændre tilstanden fra den aktuelle applikationstilstand til deres respektive tilstande.
Opdater dine ansøgningsmetoder til kodeblokkene nedenfor:
Det udkast metode:
offentligudkast(): ugyldig{
det her.state = det her.state.udkast();
det her.showCurrentState();
}
Det redigere metode:
offentligredigere(): ugyldig{
det her.state = det her.state.edit();
det her.showCurrentState();
}
Og offentliggøre metode:
offentligoffentliggøre(): ugyldig{
det her.state = det her.state.publicer();
det her.showCurrentState();
}
Brug af den færdige applikation
Din færdige ansøgningsklasse skal ligne kodeblokken nedenfor:
// Ansøgning
klasseArtikelredskaberArtikelgrænseflade{
privat tilstand: ArticleState = ny PendingDraftState();konstruktør() {
det her.showCurrentState();
}privatshowCurrentState(): ugyldig{
konsol.log(det her.stat);
}offentligtonehøjde(): ugyldig{
det her.state = det her.state.pitch();
det her.showCurrentState();
}offentligudkast(): ugyldig{
det her.state = det her.state.udkast();
det her.showCurrentState();
}offentligredigere(): ugyldig{
det her.state = det her.state.edit();
det her.showCurrentState();
}
offentligoffentliggøre(): ugyldig{
det her.state = det her.state.publicer();
det her.showCurrentState();
}
}
Du kan teste tilstandsovergangene ved at kalde metoderne i den rigtige rækkefølge. For eksempel:
konst dokumenter = ny Artikel(); // PendingDraftState: {}
docs.pitch(); // DraftState: {}
docs.draft(); // Redigeringstilstand: {}
docs.edit(); // Udgivet tilstand: {}
docs.publish(); // PendingDraftState: {}
Kodeblokken ovenfor fungerer, fordi applikationens tilstande blev overført korrekt.
Hvis du forsøger at ændre tilstanden på en måde, der ikke er tilladt, for eksempel fra pitch-tilstand til redigeringstilstand, vil applikationen give en fejl:
konst dokumenter = ny Artikel(); // PendingDraftState: {}
docs.pitch() // DraftState: {}
docs.edit() // Ugyldig handling: Kan ikke udføre opgave i nuværende tilstand
Du bør kun bruge dette mønster, når:
- Du opretter et objekt, der opfører sig anderledes afhængigt af dets aktuelle tilstand.
- Objektet har mange tilstande.
- Den tilstandsspecifikke adfærd ændrer sig ofte.
Fordele og afvejninger ved statsmønsteret
Dette mønster eliminerer omfangsrige betingede erklæringer og opretholder det enkelte ansvar og åbne/lukkede principper. Men det kan være overdrevet, hvis applikationen har få tilstande, eller dens tilstande ikke er særlig dynamiske.