《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中本來過多的條件分支。
缺點
  是會在系統中定義許多狀態類,編寫多個狀態類是一項枯燥乏味的工做,並且系統中會所以而增長很多對象。另外,因爲邏輯分散在狀態類中,雖然避開了不受歡迎的條件分支語句,但也形成了邏輯分散的問題,咱們沒法在一個地方就看出整個狀態機的邏輯。調試

 

總結

這裏由於例子如此,因此用了數組來管理,而實際的開發中,會出現各類各樣切換狀態的方式,不必定是順序的;並且可能存在兩個或者更多按鈕,他們的點擊都會致使狀態變化,這種時候,就須要根據具體的狀況,完成狀態的切換和執行相應代碼。

相關文章
相關標籤/搜索