mittens13
mittens13

Reputation: 17

how to do proper transitions between states in statemachine with provided example

i recently asked a question on how to code my statemachine in a clean way. normally i code for PLC`s so im no expert in java. i got an helpfull answer to model the states as an enum, and encapsulate the transition logic inside the enums, like this:

public enum State {

s00_StandBy {
    @Override
    public State nextState() {
        // if...
        return ...;
    }
},
s10_DetermineOperation {
    @Override
    public State nextState() {
        // if ...
        return ....;
    }
},

public abstract State nextState(); 
}

But i am having trouble implementing this myself. First thing is why cant i use certain conditions inside the enum cases?

example:

boolean resetBtn,startBtn;
State currentState;

//Transition logic
public enum State {
    //if state is s00_standBy and startBtn is true proceed to s10
    s00_StandBy {
        @Override
        public State nextState() {
        if(startBtn){ // Cant use startBtn here? how to use conditions from outside the enum case?
            return s10_DetermineOperation;
        }
         }   
    };
  
public abstract State nextState();
}

//Main
public void main() throws Exception {

//while in this state do stuff
if(currentState.equals(State.s00_StandBy)) {

//when done or condition is met go to next state
currentState.nextState();
}
}

second problem im up against is, how do you code the enum encapsulated transitions to have multiple conditions to proceed to a single new state? like normally i would program the transition sort of like this:

//state
if (currentState.equals(s10_DetermineOperation) && resetBtn = true) 
Or (currentState.equals(s20_normalOperation) && resetBtn=true)
Or (currentState.equals(s30_someOtherState) && resetBtn=true){ 
return state.s00_StandBy;}

but to my understanding wih the encapsulated enums you can only jump from a certain state to another and every transition have to be coded seperately? so you get this for the above example:

boolean resetBtn,startBtn;
State currentState;

//Transition logic
public enum State {
    //if state is s00_standBy and startBtn is true proceed to s10
    s10_DetermineOperation {
        @Override
        public State nextState() {
        if(startBtn){ // Cant use startBtn here? how to use conditions from outside the enum case?
            return s00_StandBy;
        }
         }   
    },
     s20_normalOperation {
        @Override
        public State nextState() {
        if(resetBtn){ // Cant use startBtn here? how to use conditions from outside the enum case?
            return s00_StandBy;
        }
         }   
    },
        s30_normalOperation {
        @Override
        public State nextState() {
        if(resetBtn){ // Cant use startBtn here? how to use conditions from outside the enum case?
            return s00_StandBy;
        }
         }   
    },
    
 
public abstract State nextState();
}

this gets out of hand fast when you have a lot of transitions, what am i doing wrong here?

Upvotes: 0

Views: 1007

Answers (1)

areus
areus

Reputation: 2947

There could be several solutions.

Instead of only one method for doing the transition, you could have one for evvery trigger, buttons in your case.

For example:

public abstract State startButtonPressed();
public abstract State resetButtonPressed();

And implement this methods in every state.

Another way, is to encapsulate the fields you need to access in a class, for example Context, an add it as an argument to the state transition method(s).

public abstract State nextState(Context context);


public State nextState(Context context) {
    if(context.startBtn){ 
        return s00_StandBy;
    }
}

Of course, both solutions can be combined.

Edit

An example using context:

The context class:

public class Context {

    private boolean bool1;
    private boolean bool2;

    public Context() {
    }

    public boolean isBool1() {
        return bool1;
    }

    public void setBool1(boolean bool1) {
        this.bool1 = bool1;
    }

    public boolean isBool2() {
        return bool2;
    }

    public void setBool2(boolean bool2) {
        this.bool2 = bool2;
    }
}

The enum:

public enum State {

    s00_StandBy {
        @Override
        public State nextState(Context context) {
            if (context.isBool1()) {
                return s99_otherState;
            } else {
                return s20_someOtherState;
            }
        }
    },

    s20_someOtherState {
        @Override
        public State nextState(Context context) {
            if (context.isBool2()) {
                return s99_otherState;
            } else {
                return s00_StandBy;
            }
        }
    },

    s99_otherState {
        @Override
        public State nextState(Context context) {
            return s00_StandBy;
        }
    };

    public abstract State nextState(Context context);
}

An example main method:

public class Main {

    public static void main(String... args) {

        Context context = new Context();
        State currentState = State.s00_StandBy;

        context.setBool1(true);

        currentState = currentState.nextState(context);
        System.out.println(currentState);

    }
}

Upvotes: 2

Related Questions