<aside> 💡

Het State-pattern verplaatst het gedrag van een object dat afhangt van zijn "status" naar losse klassen — één klasse per status — in plaats van dat overal in je code dezelfde grote if/else of switch terugkomt.

</aside>

Het probleem

Stel je bouwt in TrainForge een Deur waarmee een cursist tijdens een evacuatie- of klimoefening moet interacteren. Die deur kan Dicht, Open of Vergrendeld zijn, en het gedrag bij interactie hangt daar volledig van af. De voor de hand liggende, naïeve aanpak is één Interactie()-methode met een switch:

public void Interactie()
{
    switch (status)
    {
        case DeurStatus.Dicht: status = DeurStatus.Open; break;

        case DeurStatus.Open: /* niets */ break;

        case DeurStatus.Vergrendeld: Debug.Log("Op slot"); break;
    }
}

Dat werkt bij drie statussen nog wel, maar zodra er meer gedrag per status bijkomt (geluid, animatie, wie er in de VR-oefening mag interacteren) groeit die switch, en moet je 'm overal aanpassen waar het gedrag van de status afhangt.

De oplossing

Je maakt van elke status een eigen klasse die een gedeeld interface implementeert. De context-klasse (Deur) bewaart alleen een referentie naar zijn huidige status-object, en stuurt elke aanroep daarnaartoe door. Wisselen van status betekent: dat referentie-object vervangen door een ander.

De oplossing

Je maakt van elke status een eigen klasse die een gedeeld interface implementeert. De context-klasse (Deur) bewaart alleen een referentie naar zijn huidige status-object, en stuurt elke aanroep daarnaartoe door. Wisselen van status betekent: dat referentie-object vervangen door een ander.

UML-diagram

state-uml_1.png

In het diagram zie je:

Code voorbeeld (C#)

public interface IDeurState
{
    void Interactie(Deur deur);
}

public class DichtState : IDeurState
{
    public void Interactie(Deur deur)
    {
        Debug.Log("Deur gaat open.");
        deur.HuidigeState = new OpenState();
    }
}

public class OpenState : IDeurState
{
    public void Interactie(Deur deur)
    {
        Debug.Log("Deur is al open.");
    }
}

public class VergrendeldState : IDeurState
{
    public void Interactie(Deur deur)
    {
        Debug.Log("Deze deur zit op slot.");
    }
}

public class Deur
{
    public IDeurState HuidigeState { get; set; } = new DichtState();
    public void Interactie() => HuidigeState.Interactie(this);
}

Merk op: Deur.Interactie() heeft zelf geen idee meer wélke status er actief is: hij roept gewoon HuidigeState.Interactie(this) aan, en de juiste state-klasse regelt de rest, inclusief het eventueel wisselen naar een andere state.

Code voorbeeld — Unity C# (MonoBehaviour + IInteractable)

In Unity is de Deur geen los C#-object maar een VR-object in de scene waar de cursist met de controller naartoe reikt en op "interact" drukt. Het interactiesysteem van het project verwacht van elk interactief object een gedeeld interface, bijvoorbeeld IInteractable. Die interface combineer je gewoon met het State-pattern van hierboven: