samedi 3 juin 2017

Modelling a FSA in JAVA with a Strategy pattern instead of a State pattern

I figured out this way of modelling a finite state automaton in Java.

The more pruning unnecessary or redundant classes, the more I found that the State pattern wasn't required at all. Instead, a proper Strategy pattern seems to do the job quite nicely.

Class diagram

The following classes represent the FSA of a Gate (state digram below):

/**
 *
 * @author Anton Maria Prati
 */

public class GateFSA {

    GateStateBehaviour stateBehaviour = new ShutGateStateBehaviour();

    String getState(){
        return stateBehaviour.getState();
    }

    void honk(){
        transition(GateEvent.HONK);
    }

    void stay(){
        transition(GateEvent.STAY);
    }

    void alarm(){
        transition(GateEvent.ALARM);
    }

    void transition(GateEvent event){
        System.out.print(getState());
        System.out.print(" -" + event + "-> ");

        stateBehaviour = stateBehaviour.transition(event);

        System.out.println(getState());
    }
}

interface GateStateBehaviour{
    public GateStateBehaviour transition(GateEvent event);
    public String getState();
}

class ShutGateStateBehaviour implements GateStateBehaviour {

    @Override
    public GateStateBehaviour transition(GateEvent event) { 
        GateStateBehaviour nextBehaviour = this;
        if (event == GateEvent.HONK)
            nextBehaviour = new RisingGateStateBehaviour();
        if (event == GateEvent.ALARM)
            nextBehaviour = new OpenGateStateBehaviour();
        return nextBehaviour;
    }

    @Override
    public String getState() {
        return "SHUT";
    }

}

class OpenGateStateBehaviour implements GateStateBehaviour {

    @Override
    public GateStateBehaviour transition(GateEvent event) { 
        GateStateBehaviour nextBehaviour = this;
        if (event == GateEvent.HONK || event == GateEvent.STAY)
            nextBehaviour = new DescendingGateStateBehaviour();
        else if (event == GateEvent.ALARM)
            nextBehaviour = new ShutGateStateBehaviour();
        return nextBehaviour;
    }

    @Override
    public String getState() {
        return "OPEN";
    }

}

class DescendingGateStateBehaviour implements GateStateBehaviour {

    @Override
    public GateStateBehaviour transition(GateEvent event) { 
        GateStateBehaviour nextBehaviour = this;
        if (event == GateEvent.HONK)
            nextBehaviour = new RisingGateStateBehaviour();
        else if (event == GateEvent.STAY)
            nextBehaviour = new ShutGateStateBehaviour();
        return nextBehaviour;
    }

    @Override
    public String getState() {
        return "DESCENDING";
    }

}

class RisingGateStateBehaviour implements GateStateBehaviour {

    @Override
    public GateStateBehaviour transition(GateEvent event) { 
        GateStateBehaviour nextBehaviour = this;
        if (event == GateEvent.HONK)
            nextBehaviour = new DescendingGateStateBehaviour();
        else if (event == GateEvent.STAY)
            nextBehaviour = new OpenGateStateBehaviour();
        return nextBehaviour;
    }

    @Override
    public String getState() {
        return "RISING";
    }

}

enum GateEvent {
    HONK,
    STAY,
    ALARM;
}

enter image description here

Main() method:

    GateFSA gate = new GateFSA();
    gate.transition(GateEvent.HONK);
    gate.transition(GateEvent.HONK);
    gate.transition(GateEvent.STAY);
    gate.transition(GateEvent.HONK);
    gate.transition(GateEvent.STAY);
    gate.transition(GateEvent.ALARM);
    gate.transition(GateEvent.STAY);

Output:

enter image description here

I cannot seem to prefer a State Pattern over the design above. Any criticism would be greatly appreciated.

Aucun commentaire:

Enregistrer un commentaire