状态 模式 栏目 JavaScript 繁體版
原文   原文链接

level01:电灯程序

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <script>
        var Light = function(){
            this.state = 'off'; // 给电灯设置初始状态 off
            this.button = null; // 电灯开关按钮
        };
        Light.prototype.init = function(){
            var button = document.createElement( 'button' ),
                self = this;
            button.innerHTML = '开关';
            this.button = document.body.appendChild( button );
            this.button.onclick = function(){
                self.buttonWasPressed();
            }
        };
        Light.prototype.buttonWasPressed = function(){
            if ( this.state === 'off' ){
                console.log( '开灯' );
                this.state = 'on';
            }else if ( this.state === 'on' ){
                console.log( '关灯' );
                this.state = 'off';
            }
        };
        var light = new Light();
        light.init();
    </script>
</body>
</html>

  

 

缺点:javascript

  1. 很明显 buttonWasPressed 方法是违反开放封闭原则的,每次新增或者修改 light 的状态,都须要改动buttonWasPressed 方法中的代码,这使得 buttonWasPressed 成为了一个很是不稳定的方法。
  2. 在状态多的状况下buttonWasPressed方法可能会很庞大。
  3. 状态之间的切换关系,不过是往 buttonWasPressed 方法里堆砌 if 、 else 语句,增长或者修改一个状态可能须要改变若干个操做,这使 buttonWasPressed 更加难以阅读和维护。

level02:状态模式改进电灯程序

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <script>
        // OffLightState:
        var OffLightState = function( light ){
            this.light = light;
        };
        OffLightState.prototype.buttonWasPressed = function(){
            console.log( '弱光' ); // offLightState 对应的行为
            this.light.setState( this.light.weakLightState ); // 切换状态到 weakLightState
        };
        // WeakLightState:
        var WeakLightState = function( light ){
            this.light = light;
        };
        WeakLightState.prototype.buttonWasPressed = function(){
            console.log( '强光' ); // weakLightState 对应的行为
            this.light.setState( this.light.strongLightState ); // 切换状态到 strongLightState
        };
        // StrongLightState:
        var StrongLightState = function( light ){
            this.light = light;
        };
        StrongLightState.prototype.buttonWasPressed = function(){
            console.log( '关灯' ); // strongLightState 对应的行为
            this.light.setState( this.light.offLightState ); // 切换状态到 offLightState
        };
        var Light = function(){
            this.offLightState = new OffLightState( this );
            this.weakLightState = new WeakLightState( this );
            this.strongLightState = new StrongLightState( this );
            this.button = null;
        };
        Light.prototype.init = function(){
            var button = document.createElement( 'button' ),
                self = this;
            this.button = document.body.appendChild( button );
            this.button.innerHTML = '开关';
            this.currState = this.offLightState; // 设置当前状态
            this.button.onclick = function(){
                self.currState.buttonWasPressed();
            }
        };
        Light.prototype.setState = function( newState ){
            this.currState = newState;
        };

        var light = new Light();
        light.init();
    </script>
</body>
</html>

tips:

在前面的电灯例子中,咱们完成了一个状态模式程序的编写。首先定义了 Light 类, Light类在这里也被称为上下文(Context)。随后在 Light 的构造函数中,咱们要建立每个状态类的实例对象,Context 将持有这些状态对象的引用,以便把请求委托给状态对象。html

咱们要编写各类状态类, light 对象被传入状态类的构造函数,状态对象也须要持有 light 对象的引用,以便调用 light 中的方法或者直接操做 light 对象:java

var OffLightState = function( light ){
    this.light = light;
};
OffLightState.prototype.buttonWasPressed = function(){
    console.log( '弱光' );
    this.light.setState( this.light.weakLightState );
};

  


状态模式的定义:

容许一个对象在其内部状态改变时改变它的行为,对象看起来彷佛修改了它的类。 
咱们以逗号分割,把这句话分为两部分来看。第一部分的意思是将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态改变时,会带来不一样的行为变化。电灯的例子足以说明这一点,在 off和 on这两种不一样的状态下,咱们点击同一个按钮,获得的行为反馈是大相径庭的。程序员


level03:状态模式改进电灯程序

憾的是,JavaScript既不支持抽象类,也没有接口的概念。因此在使用状态模式的时候要格外当心,若是咱们编写一个状态子 
类时,忘记了给这个状态子类实现 buttonWasPressed 方法,则会在状态切换的时候抛出异常。由于 Context老是把请求委托给状态对象的 buttonWasPressed 方法。算法

不论怎样严格要求程序员,也许都避免不了犯错的那一天,毕竟若是没有编译器的帮助,只依靠程序员的自觉以及一点好运气,是不靠谱的。这里建议的解决方案跟《模板方法模式》中一致,让抽象父类的抽象方法直接抛出一个异常,这个异常至少会在程序运行期间就被发现:性能优化

var State = function(){};
    State.prototype.buttonWasPressed = function(){
        throw new Error( '父类的 buttonWasPressed 方法必须被重写' );
    };
    var SuperStrongLightState = function( light ){
        this.light = light;
    };
    SuperStrongLightState.prototype = new State(); // 继承抽象父类
    SuperStrongLightState.prototype.buttonWasPressed = function(){ // 重写 buttonWasPressed 方法
        console.log( '关灯' );
        this.light.setState( this.light.offLightState );
    };

  


状态模式的优缺点

  1. 状态模式定义了状态与行为之间的关系,并将它们封装在一个类里。经过增长新的状态类,很容易增长新的状态和转换。
  2. 避免 Context 无限膨胀,状态切换的逻辑被分布在状态类中,也去掉了 Context 中本来过多的条件分支。
  3. Context中的请求动做和状态类中封装的行为能够很是容易地独立变化而互不影响。

状态模式中的性能优化点

  1. 有两种选择来管理 state 对象的建立和销毁。第一种是仅当 state 对象被须要时才建立并随后销毁,另外一种是一开始就建立好全部的状态对象,而且始终不销毁它们。若是 state对象比较庞大,能够用第一种方式来节省内存,这样能够避免建立一些不会用到的对象并及时地回收它们。但若是状态的改变很频繁,最好一开始就把这些 state 对象都建立出来,也没有必要销毁它们,由于可能很快将再次用到它们。
  2. 在本章的例子中,咱们为每一个 Context 对象都建立了一组 state 对象,实际上这些 state对象之间是能够共享的,各Context 对象能够共享一个 state 对象,这也是享元模式的应 
    用场景之一。

状态模式和策略模式的关系

状态模式和策略模式像一对双胞胎,它们都封装了一系列的算法或者行为,它们的类图看起来几乎如出一辙,但在乎图上有很大不一样,所以它们是两种迥然不一样的模式。app

策略模式和状态模式的相同点是,它们都有一个上下文、一些策略或者状态类,上下文把请求委托给这些类来执行。函数

它们之间的区别是策略模式中的各个策略类之间是平等又平行的,它们之间没有任何联系,因此客户必须熟知这些策略类的做用,以便客户能够随时主动切换算法;而在状态模式中,状态和状态对应的行为是早已被封装好的,状态之间的切换也早被规定完成,“改变行为”这件事情发生在状态模式内部。对客户来讲,并不须要了解这些细节。这正是状态模式的做用所在。性能


level02:JavaScript 版本的状态机

状态模式是状态机的实现之一,但在 JavaScript这种“无类”语言中,没有规定让状态对象必定要从类中建立而来。另一点,JavaScript 能够很是方便地使用委托技术,并不须要事先让一个对象持有另外一个对象。下面的状态机选择了经过Function.prototype.call 方法直接把请求委托给某个字面量对象来执行。优化

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <script>
        var Light = function(){
            this.button = null;
        };
        Light.prototype.init = function(){
            var button = document.createElement( 'button' ),
                self = this;
            button.innerHTML = '已关灯';
            this.currState = FSM.off; // 设置当前状态
            this.button = document.body.appendChild( button );
            this.button.onclick = function(){
                self.currState.buttonWasPressed.call( self ); // 把请求委托给 FSM 状态机
            }
        };
        var FSM = {
            off: {
                buttonWasPressed: function(){
                    console.log( '关灯' );
                    this.button.innerHTML = '下一次按我是开灯';
                    this.currState = FSM.on;
                }
            },
            on: {
                buttonWasPressed: function(){
                    console.log( '开灯' );
                    this.button.innerHTML = '下一次按我是关灯';
                    this.currState = FSM.off;
                }
            }
        };
        var light = new Light();
        light.init();
    </script>
</body>

</html>
相关文章
相关标签/搜索
每日一句
    每一个你不满意的现在,都有一个你没有努力的曾经。
本站公众号
   欢迎关注本站公众号,获取更多信息