1. 程式人生 > >淺談js的設計模式與設計理念

淺談js的設計模式與設計理念

js設計模式(基於coder的膚淺的設計模式探究):

單例模式,單體模式,工廠模式,策略模式,模版模式,觀察-訂閱者模式,外觀模式

       理解js的設計模式,你會發現一些比較深層次的東西,會體會到js發明者的用心良苦和循序漸進,一門語言在剛出來的時候難免會有一些比較明顯的反人類的設計,但是在不斷的實踐中,這些設計反而越來越受人們推崇,這就是計算機語言的魅力所在,我們在專案中的每走一步都會無形中用到一種設計模式,這些模式潛移默化的存在在我們的js程式碼塊中,一個程式設計師如果能從設計模式角度去看待一門語言的話,

說明你真正熱愛這門語言,想去弄懂這門語言的一些基本思想,基本設計理念,js的所有設計模式都有一個共同的目的,那就是讓程式設計更加模組化,系統化,明確化,無論是哪種設計模式,我們相信,存在即合理。


一、釋出-訂閱模式(Vue的雙向繫結原理就是基於此模式)

  釋出訂閱模式的流程如下:        

  1. 確定誰是釋出者(一般是一個物件)【Vue中此模式的物件應該是例項化的vue物件本體】。

  2. 然後給釋出者新增一個快取列表,用於存放回調函式來通知訂閱者(多個物件)【Vue中的多個物件應該是繫結此資料的所有有關的標籤元素和js物件】。

  3. 釋出訊息,釋出者需要遍歷這個快取列表,依次觸發裡面存放的訂閱者回調函式,此回撥函式

     作用是用來告知訂閱者,釋出者釋出了新訊息,會觸發每一個訂閱者對應的回撥函式,來告知
     
     每一個訂閱者【Vue中,資料發生改變後我們通過此方法來通知我們的訂閱者列表裡面的相關訂閱者,以此來觸發回撥函式裡面的程式碼塊】。

 4、退訂(比如不想再接收到這些訂閱的資訊了,就可以取消掉)【Vue中不存在訂閱者退訂現象】

   此模式提供三個方法,來確保完成整個流程

   釋出訊息方法(釋出者執行)  

   訂閱者訂閱方法(訂閱者執行)  

   訂閱者退訂方法(訂閱者執行) 

二、單體模式(常用的防止變數汙染的設計模式)

       單體模式比較好理解,它是一個物件,我們建立這個物件的目地是為了系統管理程式碼中的一些公用的變數,物件,函式,避免出現變數汙染,一般我們宣告一些變數和函式的時候,我們是在封閉的函式中,一般不會造成變數汙染情況,但是以防萬一,原因是在js中,全域性變數和區域性變數的關係比較複雜(有時候還夾雜著一部分變數提升問題),不少coder經常會因為此問題出現bug老半天找不到原因,建立好這個物件以後我們只是對外暴露一個物件入口,使用裡面的變數時我們可以以object.
變數名的形式來呼叫變數,其實也是為了實現js程式碼塊的劃分名稱空間來設計的。  
   var Singleton = {
    attribute:true,
    method1:function(){},
   method2:function(){}

   };  

三、單例模式(常用的單例模式例項是彈出框)

        單例模式的概念,是指單個物件,並且此物件有且只有一個例項化物件,保證一個類只有一個

    對應的例項化物件,在例項化前會進行判斷,如果已經有了例項化物件直接return,否則才會去例項化

    物件出來,這樣做是為了保證一些邏輯性的互動的存在,舉個簡單的栗子,登陸彈出框,假如我們現在
   
    要做一個登陸彈出框,我們在點選登陸按鈕的時候,彈出框要彈出來,我們再次點選按鈕,並不會再次

    彈出一個登入框(不排出有些奇葩會去做彈出多個登入框,讓使用者一個一個去關閉的反人類互動),要

    想實現單例模式,並不輕鬆,我們可以使用閉包來實現。常用的單例模式是全域性本地快取,

    var single = (function(){

    var unique;

    function getInstance(){

    // 如果該例項存在,則直接返回,否則就對其例項化

        if( unique === undefined ){

            unique = new Construct();

        }

        return unique;
    }

    function Construct(){

        // ... 生成單例的建構函式的程式碼

    }

    return {

        getInstance : getInstance
    }

    })();


四、策略模式(遵循對修改關閉,對擴充套件開放的原則)

    策略模式主要是將演算法的使用和演算法的實現分離開,便於程式碼的維護,避免經常性的修改原始碼,

  只需要新增新程式碼即可,舉個栗子,超市賣東西,vip0.5折,老使用者0.8折,新使用者沒有折扣,

   不使用策略模式,我們只能寫一個條件判斷語句來區分使用者身份,

    function Price(personType, price) {
    //vip 5 折
    if (personType == 'vip') {
        return price * 0.5;
    } 
    else if (personType == 'old'){ //老客戶 3 折
        return price * 0.3;
    } else {
        return price; //其他都全價
    }
    } 

    這麼寫看似沒有什麼問題,但是如果我們要新增使用者身份呢,或者我們要頻繁去修改折扣呢,我們需要

   不斷的修改if else中的判斷條件了,

    使用了策略模式,看似程式碼量多了

    // 對於vip客戶
   function vipPrice() {
    this.discount = 0.5;
   }
 
    vipPrice.prototype.getPrice = function(price) {
  return price * this.discount;
    }
// 對於老客戶
function oldPrice() {
    this.discount = 0.3;
}
 
oldPrice.prototype.getPrice = function(price) {
    return price * this.discount;
}
// 對於普通客戶
function Price() {
    this.discount = 1;
}
 
Price.prototype.getPrice = function(price) {
    return price ;
}


// 上下文,對於客戶端的使用
function Context() {
    this.name = '';
    this.strategy = null;
    this.price = 0;
}
 
Context.prototype.set = function(name, strategy, price) {
    this.name = name;
    this.strategy = strategy;
    this.price = price;
}
Context.prototype.getResult = function() {
    console.log(this.name + ' 的結賬價為: ' + this.strategy.getPrice(this.price));
}


var context = new Context();
var vip = new vipPrice();
context.set ('vip客戶', vip, 200);
context.getResult();   // vip客戶 的結賬價為: 100


var old = new oldPrice();
context.set ('老客戶', old, 200);
context.getResult();  // 老客戶 的結賬價為: 60


var Price = new Price();
context.set ('普通客戶', Price, 200);
context.getResult();  // 普通客戶 的結賬價為: 200


策略模式最實用的場合就是某個“類”中包含有大量的條件性語句,比如if...else 或者 switch。每一個條件分支都會引起該“類”的特定行為以不同的方式作出改變。以其維護一段龐大的條件性語句,不如將每一個行為劃分為多個獨立的物件。每一個物件被稱為一個策略。設定多個這種策略物件,可以改進我們的程式碼質量,也更好的進行單元測試

五、外觀模式(使用最頻繁的一種模式)

    外觀模式其實就是定義一個函式,通過傳遞一些必要的引數,我們可以在函式內部封裝一些比較複雜的操作當需要通過一個單獨的函式或方法來訪問一系列的函式或方法呼叫,以簡化程式碼庫的其餘內容,使得程式碼更容易跟蹤管理或者更好的維護時,可以使用外觀模式。其實我們平時程式碼中這種模式應該是用的比較多的。