《javascript设计模式与开发实践》阅读笔记(16)—— 状态模式

状态模式

会区分事物内部的状态,事物内部状态的改变每每会带来事物的行为改变。好比电灯的开关是开仍是关,在外界的表现就彻底不一样。程序员

 

电灯例子

按照常规思路,实现一个电灯就是构造一个电灯类,而后指定一下它的开关是什么,每次开关改变,触发电灯相应的方法。数组

 1     var Light = function(){  2         this.state = 'off'; // 给电灯设置初始状态off
 3         this.button = null; // 还没有指定按钮
 4  };  5 
 6     Light.prototype.init = function(){  //初始化,创造一个按钮给电灯对象
 7         var button = document.createElement( 'button' ),  8         self = this;  //保存引用
 9         button.innerHTML = '开关'; 10         this.button = document.body.appendChild( button ); 11         this.button.onclick = function(){  //点一次开关调用一次对象的change方法
12  self.change(); 13  } 14  }; 15 
16     Light.prototype.change = function(){ 17         if ( this.state === 'off' ){ 18             console.log( '开灯' ); 19             this.state = 'on'; 20         }else if ( this.state === 'on' ){ 21             console.log( '关灯' ); 22             this.state = 'off'; 23  } 24  }; 25 
26     var light = new Light(); 27     light.init();

这段代码是很是常规的实现,逻辑上也很容易理解,但有个小问题,全部的状态是写死在change方法里的,假若咱们想要添加一些状态就得深刻进去修改,并且,想要知道对象一共有多少状态也得进去一个一个数,在代码量不少的状况下,change方法也会变得很臃肿。浏览器

 

引入模式方法前的思考

若是想要依次改变状态,并且能够知道一共有多少种状态,感受经过数组彻底能够实现,在light属性里定义一个状态数组,light的当前状态就是数组的第一个元素,每当点击时把状态改为数组的下一位。并且经过获取数组长度也很容易知道一共有多少状态,且能所有打印出来,修改起来也很方便。app

 1     var Light = function(){  2         this.stateArr=["off","on","small_light"];  //三个状态,关闭,点亮,暗一点
 3         this.state = this.stateArr[0]; // 给电灯设置初始状态off
 4         this.button = null; // 还没有指定按钮
 5  };  6     
 7     Light.prototype.change=function(){  8 
 9         var num=this.stateArr.indexOf(this.state); 10         num=(num==this.stateArr.length-1)?0:num+1; 11         this.state=this.stateArr[num]; 12         console.log( this.state ); 13     }

改动以后每次点击依次触发 "on" "small_light" "off"。函数

不过如今还有一个问题,咱们只是作到了能够依次改变状态和随便新增状态,可是change函数本质上也仅仅能够遍历状态而已,须要每种状态执行不一样的函数时,如今的代码仍是无能为力。咱们但愿的是,change函数遍历到对的状态,有一句通用的代码能够执行正确的函数。测试

因此,仅仅是把状态变成数组是不够的,咱们须要的是一个对象,里面不但存储了状态,还应该存储了相应方法。咱们统一这些方法的名称,在change函数里就能够统一调用。this

 

状态模式实现

 1     /****新建off类***/
 2     var off=function(){  3         this.statename="off";  4  }  5     off.prototype.do=function(){  6         console.log("关灯啦");  7  }  8 
 9     /****新建on类****/
10     var on=function(){ 11         this.statename="on"; 12  } 13     on.prototype.do=function(){ 14         console.log("开灯啦"); 15  } 16 
17     /****新建small_light类****/
18     var small_light=function(){ 19         this.statename="small_light"; 20  } 21     small_light.prototype.do=function(){ 22         console.log("光线变暗啦"); 23  } 24 
25     var Light = function(){ 26         this.stateArr=[];  //存储状态对象
27         this.stateArr.push(new off());    //添加状态对象,若是要增删或者调整顺序,经过操做数组或者这里手动修改均可以作到
28         this.stateArr.push(new on()); 29         this.stateArr.push(new small_light()); 30 
31         this.stateobj=this.stateArr[0];    //指定初始状态对象
32         this.state = this.stateobj.statename; // 给电灯设置初始状态
33         this.button = null;   // 还没有指定按钮
34  }; 35 
36     Light.prototype.init = function(){  //初始化,创造一个按钮给电灯对象
37         var button = document.createElement( 'button' ), 38         self = this; 39         button.innerHTML = '开关'; 40         this.button = document.body.appendChild( button ); 41         this.button.onclick = function(){  //点一次开关调用一次对象的change方法
42  self.change(); 43  } 44  }; 45 
46     Light.prototype.change = function(){ 47         var num=this.stateArr.indexOf(this.stateobj);  //返回当前状态对象在数组的索引
48         num=(num==this.stateArr.length-1)?0:num+1;     //根据索引作些处理
49 
50         this.stateobj=this.stateArr[num];    //从新指定状态对象
51         this.state=this.stateobj.statename;  //改变状态
52         this.stateobj.do();     //执行新状态对象的代码
53  }; 54 
55     var light = new Light(); 56  light.init(); 57 
58     /****点击按钮执行结果***/
59     // 开灯啦
60     // 光线变暗啦
61     // 关灯啦
62     // 开灯啦
63     //....(不断重复)

上述代码能够自行去浏览器的控制台测试,这里还有一点小问题,就是接口的统一(do方法)彻底要靠程序员的自觉和记忆力,有的时候,若是在代码较多的时候,由于没有接口统一引发bug,调试起来很麻烦,因此咱们想到了模板方法中为了不这种状况的作法。让全部状态类都产生于一个抽象类,抽象类的do方法中抛出一个错误,若是状态类没有重写该方法,在程序执行后也能很快发现错误的缘由。spa

 

状态模式的优缺点

优势
  状态模式定义了状态与行为之间的关系,并将它们封装在一个类里。经过增长新的状态类,很容易增长新的状态和转换。prototype

  避免Context(上下文)无限膨胀,状态切换的逻辑被分布在状态类中,也去掉了Context中本来过多的条件分支。
缺点
  是会在系统中定义许多状态类,编写多个状态类是一项枯燥乏味的工做,并且系统中会所以而增长很多对象。另外,因为逻辑分散在状态类中,虽然避开了不受欢迎的条件分支语句,但也形成了逻辑分散的问题,咱们没法在一个地方就看出整个状态机的逻辑。调试

 

总结

这里由于例子如此,因此用了数组来管理,而实际的开发中,会出现各类各样切换状态的方式,不必定是顺序的;并且可能存在两个或者更多按钮,他们的点击都会致使状态变化,这种时候,就须要根据具体的状况,完成状态的切换和执行相应代码。

相关文章
相关标签/搜索