代码改变世界

状态机模式

2014-08-06 20:13  Loull  阅读(1228)  评论(0)    收藏  举报

状态机

对于你的状态机,可能有若干个状态,每个状态有自己特定的属性和属于该状态下的行为,因此你可以为每个状态定义一个类,当然这些状态实现一个接口State,然后,把这些State作为状态机的实例变量

适用场合:

State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If elseif else 进行状态切换, 如果针对状态的这样判断切换反复出现,我们就要联想到是否可以采取State模式了.

特点:

·拥有一个状态机对象:用于代表当前的状态
·拥有一个切换到上/下一个状态的方法:实际上是调用了状态机自身的切换方法,由于状态机本身已经“知道”自己是什么状态,所以可以方便的切换到上、下一个状态。

 

有一个实例来辅助分析话会更方便,这里使用的实例是:编程找出文件中符合正则表达式 "alpha[alpha|digit]*" 和 "digit+" 的字符串。alpha 就是字母啦:[a-zA-Z];digit就是数字:[0-9]。

先画出DFA:

在上图中,状态只有三个(结束状态可以省略):开始状态、单词状态和数字状态。

接下来要用两种不同的方法来实现这个DFA,第一种是传统的 while&switch 大法,第二种是现在流行的设计模式中的状态模式。

为了方便比较,先抽象出一个名为Parser的接口,它提供了一个名为 parse 的方法,传统状态机状态机模式通过实现这个方法来完成对正则表达式的识别。传统状态机实现的类名叫ClassicalMachine,状态机模式的类名则叫PatternMachine。

public interface Parser {
  void parse() throws Exception ;
}

 

ClassicalMachine 类的实现非常简单,它在 parse() 方法里就完成了干完了一切:

public class ClassicalMachine implements Parser {
    private BufferedReader mFin;
    private String mToken;
     
    public ClassicalMachine( BufferedReader fin ) {
        mFin = fin;
    }
     
    private enum STATUS {START, IS_KEYWORD, IS_NUMBER};
    STATUS mStatus = STATUS.START;
 
    @Override
    public void parse() throws Exception {
        mToken = "";
        while (true) {
            int ch = mFin.read();
            if (-1 == ch)   break;
            char c = (char)ch;
 
            switch (mStatus) {
            case START:
                if (Character.isLetter(c)) {
                    mToken += c;
                    mStatus = STATUS.IS_KEYWORD;
                }
                else if (Character.isDigit(c)) {
                    mToken += c;
                    mStatus = STATUS.IS_NUMBER;
                }
                break;
 
            case IS_KEYWORD:
                if (Character.isLetterOrDigit(c)) {
                    mToken += c;
                }
                else {
                    mStatus = STATUS.START;
                    System.out.println("keyword: " + mToken);
                    mToken = "";
                }
                break;
 
            case IS_NUMBER:
                if (Character.isDigit(c)) {
                    mToken += c;
                }
                else {
                    mStatus = STATUS.START;
                    System.out.println("number: " + mToken);
                    mToken = "";
                }
                break;
            }
        }
    }
     
}

 

 注意上面有一堆 case xxx: 和 if(xxx),如果DFA中有更多的状态和动作的话,case xxx: 会更多。

接下来实现状态模式类。从DFA图可以看出需要实现的动作只有三个,这三个动作分别在碰到字母、数字和其它字符时触发,因此可以定义状态抽象类如下:

package loull.state;

public class PatternMachineDemo {
    
    public interface Parser {
        void parse(String text) throws Exception;
    }
    
    abstract class State {
        public abstract void isAlpha(char c);
        public abstract void isDigit(char c);
        public abstract void isOther(char c);
        
        public void dispatch(char c) {
            if (Character.isLetter(c)) {
                isAlpha(c);
            }
            else if (Character.isDigit(c)) {
                isDigit(c);
            }
            else {
                isOther(c);
            }
        }
    }
    
    public class PatternMachine implements Parser {
        
        private String mtoken = "";
        
        private StartState mStartState = new StartState();
        private KeywordState mKeywordState = new KeywordState();
        private NumberState mNumberState = new NumberState();
        
        private State mState = mStartState;
        
        void setState(State state) {
            mState = state;
        }
        
        State getState() {
            return mState;
        }
        
        public class StartState extends State {
            @Override
            public void isAlpha(char c) {
                setState(mKeywordState);
                mtoken += c;
            }

            @Override
            public void isDigit(char c) {
                setState(mNumberState);
                mtoken += c;
            }

            @Override
            public void isOther(char c) {
            }
        }
        
        public class KeywordState extends State {
            @Override
            public void isAlpha(char c) {
                mtoken += c;
            }

            @Override
            public void isDigit(char c) {
                mtoken += c;
            }

            @Override
            public void isOther(char c) {
                setState(mStartState);
                System.out.println("keyword: " + mtoken);
                mtoken = "";
            }
            
        }
        
        public class NumberState extends State {

            @Override
            public void isAlpha(char c) {
                setState(mStartState);
                System.out.println("Number: " + mtoken);
                mtoken = "";
            }

            @Override
            public void isDigit(char c) {
                mtoken += c;
            }

            @Override
            public void isOther(char c) {
                setState(mStartState);
                System.out.println("number: " + mtoken);
                mtoken = "";
            }
        }
        
        @Override
        public void parse(String text){
            for (char c : text.toCharArray()) {
                mState.dispatch(c);
            }
        }
        
    }//end of PatternMachine
    
    public static void main(String[] args) {
        new PatternMachineDemo().new PatternMachine().parse("JKJT&U$%&*((*JFJJ9777iuhkYU^"
                + "^&*&(*HHY*(O(J  8748974979 jjlkur98jj*&");
    }

}

 

 

 

参考:

http://obiwong.is-programmer.com/posts/19595.html

设计模式总结-State模式

Java的秘密:使用枚举建立状态机

轻量级Java有限状态机 Squirrel State Machine 0.2.5 发布

- 支持Action异步调用
- 支持声明式事件的异步派发
- 支持定时状态 (Timed State)
- 增加有限状态机内部执行状态诊断信息
- 增加@OnBeforeActionExecuted/@OnAfterActionExecuted事件监听注解
- Bug修复

https://github.com/hekailiang/squirrel

 

Squirrel State Machine 是基于Java的有限状态机的实现。主要实现功能包括:

- 提供友好的API来构建状态机
- 提供基于注释声明状态机
- 事件驱动的状态转移
- 状态机自诊断
- 状态机导出SCXML,DOT文件