Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Java中实现FSM有限状态机_Java_Design Patterns_State Machine_Fsm_State Pattern - Fatal编程技术网

如何在Java中实现FSM有限状态机

如何在Java中实现FSM有限状态机,java,design-patterns,state-machine,fsm,state-pattern,Java,Design Patterns,State Machine,Fsm,State Pattern,我有工作要做,我需要你的帮助。 我们想要实现一个FSM-有限状态机,来识别字符序列(比如:a,B,C,a,C),并判断它是否被接受 我们认为应该实现三个类:State、Event和Machine。 state类在FSM中表示一个节点,我们想用状态设计模式实现它,每个节点将从抽象类状态扩展,每个类将处理不同类型的事件并指示到新状态的转换。你认为这是个好主意吗 第二件事,我们不知道如何保存所有的转换。我们再次考虑用某种map来实现它,它保持起点,并获得下一个状态的某种向量,但我不确定这是一个好主意

我有工作要做,我需要你的帮助。 我们想要实现一个
FSM-有限状态机
,来识别字符序列(比如:a,B,C,a,C),并判断它是否被接受

我们认为应该实现三个类:
State
Event
Machine
state
类在
FSM
中表示一个节点,我们想用
状态设计模式实现它,每个节点将从抽象类状态扩展,每个类将处理不同类型的事件并指示到新状态的转换。你认为这是个好主意吗

第二件事,我们不知道如何保存所有的转换。我们再次考虑用某种
map
来实现它,它保持起点,并获得下一个状态的某种向量,但我不确定这是一个好主意

我很乐意得到一些关于如何实施它的想法,或者你可以给我一些出发点

我应该如何保存FSM,也就是说我应该如何在程序开始时构建树? 我在谷歌上搜索了一下,找到了很多例子,但没有任何帮助


非常感谢。

状态机的核心是转换表,它将状态和符号(您称之为事件)转换为新状态。这只是一个状态的双索引数组。为了健全和类型安全,将状态和符号声明为枚举。我总是以某种方式(特定于语言)添加一个“length”成员来检查数组边界。当我手工编写FSM的代码时,我会将代码格式化为行和列格式,并使用空格键。状态机的其他元素是初始状态和接受状态集。接受状态集最直接的实现是由状态索引的布尔数组。然而,在Java中,枚举是类,您可以在每个枚举值的声明中指定一个参数“accepting”,并在枚举的构造函数中初始化它

对于机器类型,可以将其作为泛型类编写。它需要两个类型参数,一个用于状态,一个用于符号,一个用于转换表的数组参数,一个用于初始状态。唯一的另一个细节(尽管很关键)是必须调用Enum.ordinal()来获取适合为转换数组编制索引的整数,因为没有语法可以直接声明带有枚举索引的数组(尽管应该有)

要抢占一个问题,
EnumMap
对转换表不起作用,因为所需的键是一对枚举值,而不是一个枚举值

enum State {
    Initial( false ),
    Final( true ),
    Error( false );
    static public final Integer length = 1 + Error.ordinal();

    final boolean accepting;

    State( boolean accepting ) {
        this.accepting = accepting;
    }
}

enum Symbol {
    A, B, C;
    static public final Integer length = 1 + C.ordinal();
}

State transition[][] = {
    //  A               B               C
    {
        State.Initial,  State.Final,    State.Error
    }, {
        State.Final,    State.Initial,  State.Error
    }
};

嗯,我建议你用Flyweight来实现这些状态。目的:避免大量小对象的内存开销。状态机可以变得非常非常大

我不确定是否需要使用设计模式状态来实现节点。状态机中的节点是无状态的。它们只是将当前输入符号与当前状态的可用转换相匹配。也就是说,除非我完全忘记了它们是如何工作的(这是绝对可能的)

如果我在编写代码,我会这样做:

interface FsmNode {
  public boolean canConsume(Symbol sym);
  public FsmNode consume(Symbol sym);
  // Other methods here to identify the state we are in
}

  List<Symbol> input = getSymbols();
  FsmNode current = getStartState();
  for (final Symbol sym : input) {
    if (!current.canConsume(sym)) {
      throw new RuntimeException("FSM node " + current + " can't consume symbol " + sym);
    }
    current = current.consume(sym);
  }
  System.out.println("FSM consumed all input, end state is " + current);
Map<Integer, Map<Symbol, Integer>> fsm; // A state is an Integer, the transitions are from symbol to state number
FsmState makeState(int stateNum) {
  return new FsmState() {
    public FsmState consume(final Symbol sym) {
      final Map<Symbol, Integer> transitions = fsm.get(stateNum);
      if (transisions == null) {
        throw new RuntimeException("Illegal state number " + stateNum);
      }
      final Integer nextState = transitions.get(sym);  // May be null if no transition
      return nextState;
    }
    public boolean canConsume(final Symbol sym) {
      return consume(sym) != null;
    }
  }
}
接口FsmNode{
公共布尔canConsume(符号sym);
公共FsmNode消费(符号sym);
//这里还有其他方法来确定我们所处的状态
}
列表输入=getSymbols();
FsmNode current=getStartState();
用于(最终符号sym:输入){
如果(!current.canConsume(sym)){
抛出新的运行时异常(“FSM节点”+current+“不能使用符号”+sym);
}
电流=电流消耗(sym);
}
System.out.println(“FSM消耗了所有输入,结束状态为“+电流”);
在这种情况下Flyweight会做什么?在FsmNode下面可能会有这样的东西:

interface FsmNode {
  public boolean canConsume(Symbol sym);
  public FsmNode consume(Symbol sym);
  // Other methods here to identify the state we are in
}

  List<Symbol> input = getSymbols();
  FsmNode current = getStartState();
  for (final Symbol sym : input) {
    if (!current.canConsume(sym)) {
      throw new RuntimeException("FSM node " + current + " can't consume symbol " + sym);
    }
    current = current.consume(sym);
  }
  System.out.println("FSM consumed all input, end state is " + current);
Map<Integer, Map<Symbol, Integer>> fsm; // A state is an Integer, the transitions are from symbol to state number
FsmState makeState(int stateNum) {
  return new FsmState() {
    public FsmState consume(final Symbol sym) {
      final Map<Symbol, Integer> transitions = fsm.get(stateNum);
      if (transisions == null) {
        throw new RuntimeException("Illegal state number " + stateNum);
      }
      final Integer nextState = transitions.get(sym);  // May be null if no transition
      return nextState;
    }
    public boolean canConsume(final Symbol sym) {
      return consume(sym) != null;
    }
  }
}
映射fsm;//状态为整数,从符号到状态编号的转换
FsmState makeState(int stateNum){
返回新的FsmState(){
公共FSM状态消费(最终符号sym){
最终映射转换=fsm.get(stateNum);
如果(转换==null){
抛出新的RuntimeException(“非法状态号”+stateNum);
}
final Integer nextState=transitions.get(sym);//如果没有转换,则可能为null
返回下一状态;
}
公共布尔canConsume(最终符号sym){
返回消耗(sym)!=null;
}
}
}
这将根据需要创建状态对象,它允许您使用更有效的底层机制来存储实际的状态机。我在这里使用的(Map(Integer,Map(Symbol,Integer)))不是特别有效的


请注意,Wikipedia页面关注的是许多相似对象共享相似数据的情况,Java中的字符串实现就是这样。在我看来,Flyweight更为通用,它涵盖了任何生命周期短的对象的按需创建(使用更多CPU以节省更高效的底层数据结构)

EasyFSM是一个动态Java库,可用于实现FSM

您可以在以下位置找到相同的文档:

此外,您还可以从以下网址下载该库:
您可以用两种不同的方式实现有限状态机

备选案文1:

具有预定义工作流的有限状态机:如果您提前知道所有状态,并且状态机几乎是固定的,并且将来不会有任何更改,则建议使用该状态机

  • 确定应用程序中所有可能的状态

  • 识别应用程序中的所有事件

  • 确定应用程序中可能导致状态转换的所有条件

  • 事件的发生可能会导致状态转换

  • 通过确定状态和转换的工作流来构建有限状态机

    e、 g如果事件1发生在状态1,则状态将更新为
    public class Action {
        private String mActionName;
    
        public Action(String actionName) {
            mActionName = actionName;
        }
    
        String getActionName() {
            return mActionName;
        }
    
        @Override
        public String toString() {
            return mActionName;
        }
    
    }
    
    public class StateImpl implements IState {
        private HashMap<String, IState> mMapping = new HashMap<>();
        private String mStateName;
    
        public StateImpl(String stateName) {
            mStateName = stateName;
        }
    
        @Override
        public Map<String, IState> getAdjacentStates() {
            return mMapping;
        }
    
        @Override
        public String getStateDesc() {
            return mStateName;
        }
    
        @Override
        public void addTransit(Action action, IState state) {
            mMapping.put(action.toString(), state);
        }
    
        @Override
        public void removeTransit(String targetStateDesc) {
            // get action which directs to target state
            String targetAction = null;
            for (Map.Entry<String, IState> entry : mMapping.entrySet()) {
                IState state = entry.getValue();
                if (state.getStateDesc().equals(targetStateDesc)) {
                    targetAction = entry.getKey();
                }
            }
            mMapping.remove(targetAction);
        }
    
    }
    
    public class FiniteStateMachineImpl implements IFiniteStateMachine {
        private IState mStartState;
        private IState mEndState;
        private IState mCurrentState;
        private ArrayList<IState> mAllStates = new ArrayList<>();
        private HashMap<String, ArrayList<IState>> mMapForAllStates = new HashMap<>();
    
        public FiniteStateMachineImpl(){}
        @Override
        public void setStartState(IState startState) {
            mStartState = startState;
            mCurrentState = startState;
            mAllStates.add(startState);
            // todo: might have some value
            mMapForAllStates.put(startState.getStateDesc(), new ArrayList<IState>());
        }
    
        @Override
        public void setEndState(IState endState) {
            mEndState = endState;
            mAllStates.add(endState);
            mMapForAllStates.put(endState.getStateDesc(), new ArrayList<IState>());
        }
    
        @Override
        public void addState(IState startState, IState newState, Action action) {
            // validate startState, newState and action
    
            // update mapping in finite state machine
            mAllStates.add(newState);
            final String startStateDesc = startState.getStateDesc();
            final String newStateDesc = newState.getStateDesc();
            mMapForAllStates.put(newStateDesc, new ArrayList<IState>());
            ArrayList<IState> adjacentStateList = null;
            if (mMapForAllStates.containsKey(startStateDesc)) {
                adjacentStateList = mMapForAllStates.get(startStateDesc);
                adjacentStateList.add(newState);
            } else {
                mAllStates.add(startState);
                adjacentStateList = new ArrayList<>();
                adjacentStateList.add(newState);
            }
            mMapForAllStates.put(startStateDesc, adjacentStateList);
    
            // update mapping in startState
            for (IState state : mAllStates) {
                boolean isStartState = state.getStateDesc().equals(startState.getStateDesc());
                if (isStartState) {
                    startState.addTransit(action, newState);
                }
            }
        }
    
        @Override
        public void removeState(String targetStateDesc) {
            // validate state
            if (!mMapForAllStates.containsKey(targetStateDesc)) {
                throw new RuntimeException("Don't have state: " + targetStateDesc);
            } else {
                // remove from mapping
                mMapForAllStates.remove(targetStateDesc);
            }
    
            // update all state
            IState targetState = null;
            for (IState state : mAllStates) {
                if (state.getStateDesc().equals(targetStateDesc)) {
                    targetState = state;
                } else {
                    state.removeTransit(targetStateDesc);
                }
            }
    
            mAllStates.remove(targetState);
    
        }
    
        @Override
        public IState getCurrentState() {
            return mCurrentState;
        }
    
        @Override
        public void transit(Action action) {
            if (mCurrentState == null) {
                throw new RuntimeException("Please setup start state");
            }
            Map<String, IState> localMapping = mCurrentState.getAdjacentStates();
            if (localMapping.containsKey(action.toString())) {
                mCurrentState = localMapping.get(action.toString());
            } else {
                throw new RuntimeException("No action start from current state");
            }
        }
    
        @Override
        public IState getStartState() {
            return mStartState;
        }
    
        @Override
        public IState getEndState() {
            return mEndState;
        }
    }
    
    public class example {
    
        public static void main(String[] args) {
            System.out.println("Finite state machine!!!");
            IState startState = new StateImpl("start");
            IState endState = new StateImpl("end");
            IFiniteStateMachine fsm = new FiniteStateMachineImpl();
            fsm.setStartState(startState);
            fsm.setEndState(endState);
            IState middle1 = new StateImpl("middle1");
            middle1.addTransit(new Action("path1"), endState);
            fsm.addState(startState, middle1, new Action("path1"));
            System.out.println(fsm.getCurrentState().getStateDesc());
            fsm.transit(new Action(("path1")));
            System.out.println(fsm.getCurrentState().getStateDesc());
            fsm.addState(middle1, endState, new Action("path1-end"));
            fsm.transit(new Action(("path1-end")));
            System.out.println(fsm.getCurrentState().getStateDesc());
            fsm.addState(endState, middle1, new Action("path1-end"));
        }
    
    }
    
    public static void main(String[] args) {
        String s = "A1@312#";
        String digits = "0123456789";
        int state = 0;
        for (int ind = 0; ind < s.length(); ind++) {
            if (state == 0) {
                if (s.charAt(ind) == '@')
                    state = 1;
            } else {
                boolean isNumber = digits.indexOf(s.charAt(ind)) != -1;
                if (state == 1) {
                    if (isNumber)
                        state = 2;
                    else if (s.charAt(ind) == '@')
                        state = 1;
                    else
                        state = 0;
                } else if (state == 2) {
                    if (s.charAt(ind) == '#') {
                        state = 3;
                    } else if (isNumber) {
                        state = 2;
                    } else if (s.charAt(ind) == '@')
                        state = 1;
                    else
                        state = 0;
                } else if (state == 3) {
                    if (s.charAt(ind) == '@')
                        state = 1;
                    else
                        state = 0;
                }
            }
        } //end for loop
    
        if (state == 3)
            System.out.println("It matches");
        else
            System.out.println("It does not match");
    }