大熊君說說JS與設計模式之------狀態模式State


一,總體概要

1,筆者淺談

狀態模式,又稱狀態對象模式(Pattern of Objects for States),狀態模式是對象的行為模式。

狀態模式主要解決的是當控制一個對象狀態的條件表達式過於復雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類中,可以把復雜的判斷邏輯簡化。 看一個例子:

 1 var TrafficLight = function () {
 2     var count = 0;
 3     var currentState = new Red(this);
 4  
 5     this.change = function (state) {
 6         // limits number of changes
 7         if (count++ >= 10) return;
 8         currentState = state;
 9         currentState.go();
10     };
11  
12     this.start = function () {
13         currentState.go();
14     };
15 }
16 var Red = function (light) {
17     this.light = light;
18  
19     this.go = function () {
20         log.add("Red --> for 1 minute");
21         light.change(new Green(light));
22     }
23 };
24 var Yellow = function (light) {
25     this.light = light;
26  
27     this.go = function () {
28         log.add("Yellow --> for 10 seconds");
29         light.change(new Red(light));
30     }
31 };
32 var Green = function (light) {
33     this.light = light;
34  
35     this.go = function () {
36         log.add("Green --> for 1 minute");
37         light.change(new Yellow(light));
38     }
39 };
40 var log = (function () {
41     var log = "";
42  
43     return {
44         add: function (msg) { log += msg + "\n"; },
45         show: function () { alert(log); log = ""; }
46     }
47 })();
48  
49 function run() {
50     var light = new TrafficLight();
51     light.start();
52  
53     log.show();
54 }

在示例代碼中分為三種狀態:紅色,黃色,綠色

我們的例子是一個交通燈(即信號燈對象)有3種不同的狀態:紅色,黃色和綠色,各有自己的一套規則。規則是這樣的:交通燈是紅色的。延遲紅狀態變為綠色狀態后。然后,一段時間后,綠色的狀態更改為黃色的狀態。

一個非常短暫的延遲黃色狀態變為紅色后,等等。

需要注意的是,它是對象狀態決定下一個狀態過渡的重要。這也是對象狀態變化,交通燈的當前狀態------沒有交通燈本身。

出於演示的目的,一個內置的計數器記錄狀態發生變化的數量,否則程序會運行下去。日志功能是一個幫助它收集並顯示結果。

 

二,源碼案例參考

 

采用讓狀態對象來維護和轉換狀態的調用順序

1)調用上下文的方法來處理業務請求。

2)獲取State對象。

3)委托讓相應的狀態對象去處理。

4)調用方法得到下一個狀態方法。

5)設置下一個狀態方法。

6)再到5),直到結束。

 

三,案例引入

再次強調一下狀態模式的意圖

Context: 環境,也稱上下文,通常用來定義客戶感興趣的接口,同時維護一個具體處理當前狀態的實例對象。

State:狀態接口,用來封裝與上下文的一個特定狀態所對應的行為。

ConcreteState: 具體實現狀態處理的類,每個類實現一個跟上下文相關的狀態的具體處理。

 

例子場景:

 

模擬工作流中的請假流程,流程是這樣的:

 

當某人提出請假申請,先由項目經理審批,

 

如果項目經理不同意,審批就直接結束;

 

如果項目經理同意了,再看請假的天數是否超過3天,項目經理的審批權限只有3天以內,

 

如果請假天數在3天以內,那么審批也直接結束,

 

否則就提交給部門經理,部門經理審核過后,無論是否同意,審批都直接結束。

 

 

 1 (function () {
 2     // 模擬工作流
 3 
 4     // 定義請假單的業務數據模型
 5     function LeaveRequestModel() {
 6         // 請假人
 7         this.user = '';
 8         // 請假開始時間
 9         this.beginDate = '';
10         // 請假天數
11         this.leaveDays = '';
12         // 審核結果
13         this.result = '';
14     }
15 
16     /*
17     請假流程,需項目經理和部門經理審批
18      */
19 
20     function LeaveRequestContext2() {
21         this.state = null;
22         // 包含流程處理需要的業務數據對象
23         this.businessVO = null;
24         this.doWork = function () {
25             if (typeof this.state == 'function') {
26                 this.state = this.state(this);
27                 this.doWork();
28             }
29         };
30     }
31 
32     function projectManagerState(request) {
33         var leaveRequestModel = request.businessVO;
34 
35         console.log('項目經理審核中,請稍候。。');
36         console.log(leaveRequestModel.user + '申請從'
37             + leaveRequestModel.beginDate + '開始請假'
38             + leaveRequestModel.leaveDays + '天,請項目經理審核(1為同意,2為不同意)');
39 
40         var answer = window.prompt('1為同意,2為不同意');
41         var result = answer == 1 ? '同意' : '不同意';
42         leaveRequestModel.result = '項目經理審核結果:' + result;
43 
44         var state;
45         if (answer == 1) {
46             if (leaveRequestModel.leaveDays > 3) {
47                 state = depManagerState;
48             } else {
49                 state = auditOverState;
50             }
51         } else {
52             state = auditOverState;
53         }
54 
55         return state;
56     }
57 
58     function depManagerState(request) {
59         var leaveRequestModel = request.businessVO;
60 
61         console.log('部門經理審核中,請稍候。。');
62         console.log(leaveRequestModel.user + '申請從'
63             + leaveRequestModel.beginDate + '開始請假'
64             + leaveRequestModel.leaveDays + '天,請項目經理審核(1為同意,2為不同意)');
65 
66         var answer = window.prompt('1為同意,2為不同意');
67         var result = answer == 1 ? '同意' : '不同意';
68         leaveRequestModel.result = '部門經理審核結果:' + result;
69 
70         return auditOverState;
71     }
72 
73     function auditOverState(request) {
74         var leaveRequestModel = request.businessVO;
75         // do sth
76         console.log(leaveRequestModel.user + ',你的請假申請已經審核結束,結果是:' + leaveRequestModel.result);
77     }
78 
79     var lrm = new LeaveRequestModel();
80     lrm.user = '小林';
81     lrm.beginDate = '2014-4-2';
82     lrm.leaveDays = 5;
83 
84     var request = new LeaveRequestContext2();
85     request.businessVO = lrm;
86     request.state = projectManagerState;
87 
88     request.doWork();
89 
90 }());

 

 

四,總結一下

認識狀態模式

  使用場景

 

  State模式在實際使用中比較多,適合"狀態的切換"。因為我們經常會使用If else if else 進行狀態切換,如果針對狀態的這樣判斷切換反復出現,我們就要聯想到是否可以采取State模式了。

  不只是根據狀態,也有根據屬性。如果某個對象的屬性不同,對象的行為就不一樣,這點在Database系統中出現頻率比較高,我們經常會在一個數據表的尾部,加上property屬性含義的字段,

  用以標識記錄中一些特殊性質的記錄,這種屬性的改變(切換)又是隨時可能發生的,就有可能要使用State。

 

 

  狀態和行為

  所謂對象的狀態,通常指的就是對象實例的屬性的值;而行為指的就是對象的功能,再具體點說,行為大多可以對應到方法上。

  狀態模式的功能就是分離狀態的行為,通過維護狀態的變化,來調用不同狀態對應的不同功能。也就是說,狀態和行為是相關聯的,它們的關系可以描述為:狀態決定行為

  由於狀態是在運行期被改變的,因此行為也會在運行期根據狀態的改變而改變。

  行為的平行性

  注意平行線而不是平等性。所謂平行性指的是各個狀態的行為所處的層次是一樣的,相互獨立的、沒有關聯的,是根據不同的狀態來決定到底走平行線的哪一條。

  行為是不同的,當然對應的實現也是不同的,相互之間是不可替換的。

  環境和狀態處理對象

  在狀態模式中,環境(Context)是持有狀態的對象,但是環境(Context)自身並不處理跟狀態相關的行為,而是把處理狀態的功能委托給了狀態對應的狀態處理類來處理。

  在具體的狀態處理類中經常需要獲取環境(Context)自身的數據,甚至在必要的時候會回調環境(Context)的方法,因此,通常將環境(Context)自身當作一個參數傳遞給具體的狀態處理類。

  客戶端一般只和環境(Context)交互。客戶端可以用狀態對象來配置一個環境(Context),一旦配置完畢,就不再需要和狀態對象打交道了。客戶端通常不負責運行期間狀態的維護,也不負責決定后續到底使用哪一個具體的狀態處理對象。

  

 

 

                                           哈哈哈,本篇結束,未完待續,希望和大家多多交流夠溝通,共同進步。。。。。。呼呼呼……(*^__^*)            

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM