会区分事物内部的状态,事物内部状态的改变每每会带来事物的行为改变。好比电灯的开关是开仍是关,在外界的表现就彻底不一样。程序员
按照常规思路,实现一个电灯就是构造一个电灯类,而后指定一下它的开关是什么,每次开关改变,触发电灯相应的方法。数组
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中本来过多的条件分支。
缺点:
是会在系统中定义许多状态类,编写多个状态类是一项枯燥乏味的工做,并且系统中会所以而增长很多对象。另外,因为逻辑分散在状态类中,虽然避开了不受欢迎的条件分支语句,但也形成了逻辑分散的问题,咱们没法在一个地方就看出整个状态机的逻辑。调试
这里由于例子如此,因此用了数组来管理,而实际的开发中,会出现各类各样切换状态的方式,不必定是顺序的;并且可能存在两个或者更多按钮,他们的点击都会致使状态变化,这种时候,就须要根据具体的状况,完成状态的切换和执行相应代码。