如何使用提供的示例在状态机中的状态之间进行适当的转换

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

我最近问了一个关于如何以干净的方式编写状态机代码的问题。通常我为 PLC 编写代码,所以我不是 java 方面的专家。 我得到了一个有用的答案,将状态建模为枚举,并将转换逻辑封装在枚举中,如下所示:

public enum State {

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

public abstract State nextState(); 
}

但我自己实施时遇到了问题。 首先是为什么我不能在枚举案例中使用某些条件?

示例:

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();
}
}

我遇到的第二个问题是,如何编写枚举封装的转换代码,使其具有多个条件以继续进入单个新状态? 像往常一样,我会像这样对过渡进行编程:

//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;}

但是根据我的理解,封装的枚举只能从某个状态跳到另一个状态,并且每个转换都必须单独编码? 所以你在上面的例子中得到了这个:

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();
}

当你有很多转换时,这很快就会失控,我在这里做错了什么?

可能有多种解决方案。

在您的情况下,您可以为每个触发器、按钮使用一种方法,而不是只有一种方法来进行转换。

例如:

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

并在每个州实施此方法。

另一种方法是将您需要访问的字段封装在 class 中,例如 Context,并将其作为参数添加到状态转换方法中。

public abstract State nextState(Context context);


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

当然,这两种解决方案可以结合使用。

编辑

使用上下文的示例:

上下文 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;
    }
}

枚举:

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);
}

主要方法示例:

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);

    }
}