聽飛狐聊JavaScript設計模式系列13

本回內容介紹

上一回聊了聊組合模式(Composite),用組合模式模擬了個圖片庫,聊了遞歸。
介一回聊狀態模式(State),官方描述容許一個對象在其內部狀態改變時改變它的行爲。略抽象,不過看了代碼會以爲比較簡單,直接看代碼先。javascript

1. 狀態模式

狀態模式中引入了抽象狀態類和具體狀態類,這裏會用到以前講過的接口模式,不清楚的盆友能夠看系列05,這個例子是我根據一段PHP的狀態模式代碼改的JS版本,以下:前端

var c = console,d = document;
window.onload = function(){
    // 定義了一個state接口,倆方法handle,display
    var State = new Interface("State",["handle","display"]);
    // 這裏的Context是環境類,或者稱爲上下文類,是擁有多種狀態的對象。
    var Context = function(state){
        // 狀態變化的屬性
        var _state = null;
        var _self = this;
        // 這裏是設置狀態對象
        this.setState = function(state){
            _self._state = state;
        }
        // 調用狀態對象的方法  
        this.request = function(){
            _self._state.display();
            _self._state.handle(_self);
        }
        this.setState(state);
        
    }
    
    // 具體狀態類,抽象狀態類的子類
    var StateA = function(){
        // 檢測接口,不明白的童鞋看系列05
        Interface.ensureImplements(this,State);
    }
    // 狀態類的實現
    StateA.prototype = {
        constructor:StateA,
        // 這裏的context指向的是Context
        handle:function(context){
            // 設置爲其餘狀態
            context.setState(new StateB());
        },
        // 具體實現的方法
        display:function(){
            c.log('StateA');
        }
    }
    // StateB跟StateA同樣,只是狀態設置不一樣
    var StateB = function(){    
        Interface.ensureImplements(this,State);
    }
    StateB.prototype = {
        constructor:StateB,
        handle:function(context){
            context.setState(new StateC());
        },
        display:function(){
            c.log('StateB');
        }
    }
    // StateC跟StateB同樣,只是狀態設置不一樣
    var StateC = function(){    
        Interface.ensureImplements(this,State);
    }
    StateC.prototype = {
        constructor:StateC,
        handle:function(context){
            context.setState(new StateA());
        },
        display:function(){
            c.log('StateC');
        }
    }
    // 測試代碼
    var o = new Context(new StateB());
    o.request();    // StateB
    o.request();    // StateC
    o.request();    // StateA
    o.request();    // StateB
    o.request();    // StateC
    o.request();    // StateA
}

之因此把這個例子改爲JS版本,是由於這個例子比較簡單,直觀。在實際使用時,在一個狀態類中可能包含多個業務方法,若是在具體狀態類中某些業務方法的實現徹底相同,能夠將這些方法移至抽象狀態類,實現代碼的複用。vue

2. 狀態模式之工做流

狀態模式在工做流或遊戲等各類系統中應用比較普遍,下面的例子是一個簡單的訂單審批流程,根據一段Java的狀態模式代碼改的JS版本,以下:java

var c = console,w = window;
w.onload = function(){
    // 業務申請方
    function Obj() {
        // 申請方
        this.obj = '';
        // 日期
        this.sDate = '';
        // 金額
        this.aMount = '';
        // 結果
        this.res = '';
    }

    // 跟上一個例子同樣,Context狀態管理類
    var Context = function (s) {
        this.state = null;
        this.run = function (s) {
            this.state = s;
        };
        this.run(s);
    }

    // 第一層級審覈
    var StateA = function (o) {
        // 這一堆就是一個簡單交互
        var reply = w.prompt('贊成請按1,不一樣意請按任意鍵');
        var res = reply == 1 ? '贊成' : '不一樣意';
        var state;
        o.res = res;
        // 一堆打印信息
        c.log(
            '平臺審覈中...' + 
            '申請方:' + o.obj + ' ' + 
            '日期:' + o.sDate + ' ' + 
            '金額:' + o.aMount + ' ' + 
            '平臺審覈結果:' + o.res
            );
        // 判斷是否贊成,若是不一樣意直接走Notice提示不一樣意
        if (reply == 1) {
            // 這裏就是走第二層級
            state = new StateB(o);
        } else {
            state = new Notice(o);
        }

        return state;
    }
    
    // 第二層級審覈,跟第一層級差很少
    var StateB = function (o) {
        var reply = w.prompt('贊成請按1,不一樣意請按任意鍵');
        var res = reply == 1 ? '贊成' : '不一樣意';
        var state;
        o.res = res;
        c.log(
            '商家審覈中...' + 
            '申請方:' + o.obj + ' ' + 
            '日期:' + o.sDate + ' ' + 
            '金額:' + o.aMount + ' ' + 
            '商家審覈結果:' + o.res
            );
        // 由於是最終審覈,因此直接走Notice提示
        state = new Notice(o);
        return state;
    }

    // Notice通知類,打印信息
    var Notice = function (o) {
        c.log('申請方:' + o.obj + '的流程結束,審覈結果爲:' + o.res);
    }

    // 測試
    var o = new Obj();
    o.obj = '大衛';
    o.sDate = '聖誕節';
    o.aMount = 250;
    
    var req = new Context(new StateA(o));
    req.run();
};

這個例子跟上一個例子都是狀態模式的一個很好體現,這個例子更貼近實際開發,
申請方提交訂單,平臺訂單審覈,審覈經過更改狀態爲審覈經過,而後繼續商家中心審覈,經過與不經過都流程結束,直接調用提示類提示結果。react

3. 有限狀態機

有限狀態機(Finite-state machine)是一個很是有用的模型,能夠模擬世界上大部分事物。這個是官方說法,簡單說,她有三個特徵:
1,狀態總數(state)是有限的。
2,任一時刻,只處在一種狀態之中。
3,某種條件下,會從一種狀態轉變(transition)到另外一種狀態。
直接代碼吧,很直觀的,根據網上的交通燈例子改的,以下::canvas

var c = console,d = document;
window.onload = function(){
    var Light = function() {
        // 設置默認狀態,關燈
        this.currentState = State.off;
        this.lightSwitch = null;
    };

    Light.prototype.run = function() {
        var _self = this;
        // 這裏是開關
        var lightSwitch = d.createElement('button');
        // 這個canvas就當是一盞燈了
        var cvs = d.createElement('canvas');
        cvs.width = '200';
        cvs.height = '200';
        cvs.style.backgroundColor = 'lightblue';
        cvs.style.borderRadius = '50%';
        cvs.style.display = 'block';
        // 初始值是開燈
        lightSwitch.innerHTML = '開燈';
        // Dom方法appendChild沒什麼好說的
        this.lightSwitch = d.body.appendChild(lightSwitch);
        this.cvs = d.body.appendChild(cvs);
        // 開關的點擊事件
        this.lightSwitch.onclick = function() {
            // 委託請求給State狀態機,這裏分兩步走,第一步是_self.currentState,第二步是btnPress,call用來指向將做用域指向自己
            _self.currentState.btnPress.call(_self);
        };
    };

    // 這裏就是多種狀態間的切換,這裏只寫了兩種,開關燈
    var State = {
        off: {
            btnPress: function() {
                this.lightSwitch.innerHTML = '關燈';
                this.cvs.style.display = 'none';
                this.currentState = State.on;
            }
        },
        on: {
            btnPress: function() {
                this.lightSwitch.innerHTML = '開燈';
                this.cvs.style.display = 'block';
                this.currentState = State.off;
            }
        }
    };
    // 測試代碼
    var light = new Light();
    light.run(); 
};

這裏就是多種狀態間的切換,這裏只寫了兩種,開關燈,好玩兒一點能夠換canvas的顏色,作變亮變暗的狀態。前端框架

裝逼圖
裝個逼,雖然最近熱映的《尋龍訣》好看,仍是要推薦一個阿湯哥的老電影《遺忘戰境》~~app

這一回聊的狀態模式,有限狀態機,狀態模式能夠容許客戶端改變狀態的轉換行爲,而狀態機則是可以自動改變狀態~。
下面的內容,聊一下狀態管理,這裏拿一個極具審美(由於做者常常提審美^_^)的JS庫Vue.js的官方例子來講。框架

JS狀態管理

Vue官方解釋是集中管理狀態更易於理解狀態將怎樣變化。組件仍然能夠擁有和管理它的私有狀態。看過上面的例子,看這個應該很好理解,繼續寫:函數

// 這裏的store裏面放了全部的action
var store = {
  state: {
    message: 'Hello!'
  },
  // 這裏的action則用來修改store的狀態state
  actionA: function () {
    this.state.message = 'action A triggered'
  },
  // 這裏跟上面的例子很像
  actionB: function () {
    this.state.message = 'action B triggered'
  }
}
// 這個new Vue暫時不用管,簡單的解釋下吧,經過構造函數Vue建立一個Vue的根實例
var vmA = new Vue({
  data: {
    privateState: {},
    sharedState: store.state
  }
})

var vmB = new Vue({
  data: {
    privateState: {},
    sharedState: store.state
  }
})

這是vue.js官網上的一個例子,最近很火的框架react,vue.js在說flux單向數據流,其實就是在維護狀態。

話外音

這裏囉嗦一下,有盆友建議說聊一下AngularJS,在系列04裏有聊過一點點ng1.x的源碼,也模擬了一下,而且說過能夠的話,分享讀過的Angularjs源碼段子。介於前端框架風起雲涌,其實,對Angular有興趣的盆友,能夠直接從Angular2開始,若是是學習源碼的話,Vue.js的源碼讀起來會更有美感(由於做者反覆提審美^_^)~~

這一回,主要聊了狀態模式,有限狀態機,介紹了下Vue.js這個庫。
下一回,聊一聊JS的鏈式調用,沒錯,確定會模擬框架,庫什麼的。

客觀看完點個贊,推薦推薦唄,嘿嘿~~


注:此係飛狐原創,轉載請註明出處

相關文章
相關標籤/搜索