前端經常使用的設計模式剖析——單例、觀察者、工廠、策略模式

什麼是設計模式

有人說設計模式是能被反覆使用、多數人知道的、通過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼,讓代碼更容易被他人理解、保證代碼的可靠性。html

我的認爲,設計模式其實就是前端工程化編程的一種思想,將代碼 可以理解成更貼近生活的一種經驗。年紀大了,就知道作什麼,怎麼作,怎麼能作的更好,編程也同樣,時間長了,就更明白什麼需求應該採用什麼方式編寫,這種一向的編寫方式因而被總結爲設計模式,咱們如今用的這些模式,其實就是前輩的經驗總結,能避免咱們繞彎路,更好的編程。前端

設計模式有不少

  • 單體/單例模式
  • 觀察者模式
  • 工廠模式
  • 策略模式
  • 模板模式
  • 代理模式
  • 外觀模式

....
咱們經常使用的也就是 單體模式、工廠模式、單例模式、觀察者模式、策略模式,其餘的可能沒用到,或者無形中用到了但沒注意是什麼模式,無論是什麼模式,可以被重用的,可維護的代碼,都是好的程序vue

1、單體/單例模式

單例模式在js中咱們隨處都見,一個類只能保證有一個實例,例如對象字面量的方式建立一個單例,他能夠定義不少的屬性和方法,這個類也只有一個實例對象。優勢,可以單獨劃分一個命名空間,避免和別人的內部變量發生衝突,因此單例能夠分爲簡單的單例和閉包單例編程

  • 簡單單例segmentfault

    //先判斷實例是否存在,存在則返回,不存在則建立,這樣能夠保證一個類只有一個實例對象
       var test_simple = test_simple || {
       name: 'alice',
       age: 15,
       gender: '2',
       sayName: function(){
           console.log('my name is ' + this.name)
       },
       sayAge: function(){
           console.log('i am '+ this.age + ' years old')
       }
    }
  • 閉包單例設計模式

    閉包的做用是保護一些私有屬性,不讓外界訪問,只有return將屬性暴露才能被外界訪問
       var test_Closure = test_Closure || {
       introduce = (function(){
           var _name = 'bob',          //定義私有屬性
           var _age = 18,
           var _gender = '1',
           var _sayName = function(){
               console.log('my name is ' + _name)
           },
           var _sayAge = function(){
               console.log('i am '+ _age + ' years old')
           }
           
           //將屬性暴露 讓別人看看
           return {
               name: _name,
               age : _age,
               gender : _gender,
               sayName : function(){
                   return _sayName();
               },
               sayAge : function(){
                   return _sayAge();
               }
           }
       })()
    }
    
    //調用
    test_Closure.sayName();      =>'my name is bob' 
    test_Closure.sayAge();       =>'i am 18 years old'

2、觀察者模式

例如vue的雙向數據綁定的原理:前端工程化

當咱們在表單輸入框中輸入(發佈)message的時候,依賴(訂閱)他的地方都會被更改
一句話描述:一個頁面在多處訂閱使用了同一個數據,用Object.defineProperty監聽其改變,並由發佈者通知 訂閱者 去更新它所持有的數據數組

具體實現請參照 https://segmentfault.com/a/11...閉包

實現一個分蛋糕案例

一、onserver.jsapp

var Observer = {}; //定義一個對象 包括三個方法 訂閱 發佈 退訂
(function (_observer) {

    var subListObj = {}, // 回調函數存放的數組
        subId = -1; //訂閱者id
    // 發佈  傳入兩個參數 (訂閱主題,具體內容)
    _observer.publish = function (subTip, args) {
        if (!subListObj[subTip]) {
            return false;      //判斷是否有訂閱者
        }

        setTimeout(function () {
            var subscribers = subListObj[subTip],   //定義一個數組用來存儲全部訂閱者
                len = subscribers ? subscribers.length : 0;

            while (len--) {                          //只要發佈者一發布就會遍歷全部訂閱者,分發信息
                subscribers[len].func(subTip, args);  
            }
        }, 0);

        return true;

    };
    //訂閱
    _observer.subscribe = function (subTip, func) {

        if (!subListObj[subTip]) {
            subListObj[subTip] = [];
        }

        var token = (++subId).toString();   //訂閱者惟一標識
        subListObj[subTip].push({                //接收信息
            token: token,             
            func: func                      //func不只是一個動做 數據更新的回調
        });
        // console.log(token)      // => {example1:[0,func]}{example1:[1,func]}
        return token;
    };
    //退訂
    _observer.unsubscribe = function (token) {     //退訂 傳入訂閱者的id進行過濾 若是退訂就splice刪除
        for (var m in subListObj) {
            if (subListObj[m]) {
                for (var i = 0, j = subListObj[m].length; i < j; i++) {
                    if (subListObj[m][i].token === token) {
                        subListObj[m].splice(i, 1);
                        console.log('我' + token + '不吃了')
                        return token;
                    }
                }
            }
        }
        return false;
    };
} (Observer));

二、過來吃蛋糕啦 index.html

<script src="./js/observer.js"></script>
    <script>
        //來,訂閱一個
        Observer.subscribe('subCake', function (subTip, data) {
            console.log(subTip + ": " + data);
        });
        //來,再訂閱一個
        Observer.subscribe('subCake', function (subTip, data) {
            console.log(subTip + "我來啦.." + data);
        });
        //0不吃了
        Observer.unsubscribe('0')
        //發佈通知
        Observer.publish('subCake', '快來分蛋糕...');
    </script>
    
      若是0沒有退訂:
         subCake我也收到了..快來分蛋糕...
         subCake: 快來分蛋糕...
            
      0退訂了:
         我0不吃了
         subCake我也收到了..快來分蛋糕...

3、工廠模式

工廠模式:提供建立對象的接口,封裝一些公用的方法,若是實現具體的業務邏輯,能夠放在子類重寫父類的方法
優勢:弱化對象間的耦合,防止代碼重複
缺點:簡單業務能夠用,複雜的業務會致使代碼維護性差,不易閱讀

//聲明一個蛋糕店 負責作蛋糕 和 賣蛋糕
var CakeShop = function(){}

CakeShop.prototype = {
    sellCake: function(){

    },
    makeCake: function(type){
        console.log('aaa')
    }
}

   //定義一個繼承的方法
var Extend = function(desc, src){
    for(var property in src){
        desc[property] = src[property]
    }
    return desc;
}
Object.extend = function(obj){
    return Extend.apply(this,[this.obj])
}

 //聲明一個水果蛋糕,從蛋糕店
var FruitCake = function(){}
Object.extend(FruitCake, CakeShop);

console.log(FruitCake.prototype)

FruitCake.prototype.makeCake = function(type){
    var cake;
    switch (type){
        case 'apple':
            cake = new AppleCake();break;
        case 'pear':
            cake = new Pear();break;
        default:
            cake = new Orange();break;
    }
    return cake;
}

var buyCake = new FruitCake();
var myCake = buyCake.sellCake('apple')
console.log(myCake)

策略模式

策略模式是一種很好的編程思想,能夠用來解決多個if條件語句,可以代碼複用,邏輯清晰,也容易擴展。
來來來,上代碼

//之前你是這樣的
var buyCar = function(brand){
    if(brand == '奧迪') {
        return '大於80萬'
    }
    if(brand == 'QQ') {
        return '小於10萬'
    }
    if(brand == 'ofo') {
        return '小於1000元'
    }
}
console.log(buyCar('ofo'))   // => 小於1000元

//那麼如今你應該這樣
var buyCars = {
    '奧迪': function(){
        return '大於80萬'
    },
    'QQ': function(){
        return '小於10萬'
    },
    'ofo': function(){
        return  '小於1000元'
    }
}
var result = function(brand){
    return buyCars[brand]();
}
console.log(result('ofo'))   // => 小於1000元

// 系不繫很簡單

設計模式還有不少,有時間還會繼續學習...

相關文章
相關標籤/搜索