Patternskolen del 6 – State

State pattern er en elegant løsning på Shotgun Surgery.

Bjørn Herve Moslet

I forrige del av serien så vi på Strategy. Denne gangen tar vi for oss et pattern som ligner.

Har du skrevet kode som dette?

switch (kontekst.state)
{
case State.Ny:
// Gjør noe
break;
case State.Åpen:
// Gjør noe annet
break;
case State.Lukket:
// Ikke lov å gjøre noe
break;
}

Gjøres dette flere plasser i koden drar det med seg store vedlikeholdsproblemer (også kjent som shotgun surgery).

State pattern

State (følg lenka for beskrivelse og eksempler i C#, Java med flere) er en elegant løsning på dette problemet. State er et behavioral pattern, som mange av dem vi har sett på tidligere. Som navnet tilsier, så representerer en klasse en tilstand i koden. Logikk som kan utføres i den gitte tilstanden er innkapslet i tilstandsklassen og det samme gjelder for overgangene mellom tilstandene.

state

Dette UML-diagrammet gir et eksempel på patternet. Kontekst er i dette tilfellet den klassen som har en tilstand. Kontekst har en property av den abstrakte typen AbstraktState og denne holder referansen til den gjeldende tilstanden. AbstraktState har en referanse til Kontekst. I tillegg finnes tre konkrete implementasjoner av tilstandene Ny, Åpen og Lukket.

Ved instansiering av Kontekst blir state satt til Ny i konstruktøren. Når en operasjon som påvirker tilstand utføres på Kontekst (i dette tilfellet Tilbake eller Neste), vil kallet bli delegert til den aktuelle metoden på state-propertyen. Hver klasse kan ha forskjellig implementasjoner av Tilbake og Neste. Hvis en metode endrer tilstanden til Kontekst vil den sette Kontekst state-property til en referanse til et annen tilstandsobjekt.

En kan se for seg at Tilbake-metoden i New-klassen ikke gjør noe som helst, mens Neste-metoden bl.a. endrer tilstand på Kontekst til Åpen. På samme vis vil Åpens Tilbake endre tilstand til Ny, mens Neste-metoden endrer tilstand til Lukket. For Lukket gjør Neste-metoden ingenting, mens Tilbake endrer tilstand tilbake til Åpen. Eksempel på dette vises under:

void Tilbake()
{
// Gjør noe kult med kontekst

kontekst.state = new Åpen(kontekst);
}

Dette er ikke den eneste måten å implementere State patternet på, og ikke nødvendigvis den beste, men illustrerer poenget.

Oppsummering

State bruker klasser for å representere tilstanden i et objekt. Fordelen med dette er at all tilstandsavhengig logikk, og overgangen mellom tilstandene, samles ett sted i kodebasen. Det gjør tilstandskoden lett å forstå og vedlikeholde.

Det er litt overhead med å bruke State patternet, både i implementasjon og ved databaselagring. Det er derfor ikke nødvendigvis nyttig i trivielle scenarier.

State vs. Strategy

State kan også minne om Strategy (les mer i del 4 og del 5 av denne serien), men det er forskjeller:

  • State-objekter har referanse til instansene som eier dem
  • State-objekter kan bytte ut seg selv med andre tilstander
  • Strategy «injectes» i objektene (kontekst), mens State opprettes av objektene selv
  • Strategy gjør gjerne bare én ting, men State inneholder hele tilstanden og kan bestå av mange metoder