前端也要學系列:設計模式之策略模式

作前端開發已經好幾年了,對設計模式一直沒有深刻學習總結過。隨着架構相關的工做愈來愈多,愈來愈能感受到設計模式成爲了我前進道路上的一個阻礙。因此從今天開始深刻學習和總結經典的設計模式以及面向對象的幾大原則。

今天第一天,首先來說策略模式。javascript

什麼是策略模式?

GoF四兄弟的經典《設計模式》中,對策略模式的定義以下:前端

定義一系列的算法,把它們一個個封裝起來,而且使它們可互相替換。

上邊這句話,從字面來看很簡單。可是如何在開發過程當中去應用,僅憑一個定義依然是一頭霧水。以筆者曾經作過的商戶進銷存系統爲例:java

某超市準備舉行促銷活動,市場人員通過調查分析制定了一些促銷策略:
  1. 購物滿100減10
  2. 購物滿200減30
  3. 購物滿300減50
  4. 。。。

收銀軟件的界面是這樣的(簡單示意):程序員

圖片描述

咱們應該如何計算實際消費金額?算法

最初的實現是這樣的:設計模式

//方便起見,咱們把各個促銷策略定義爲枚舉值:0,1,2...
var getActualTotal = function(onSaleType,originTotal){
    if(onSaleType===0){
        return originTotal-Math.floor(originTotal/100)*10
    }
    if(onSaleType===1){
        return originTotal-Math.floor(originTotal/200)*30
    }
    if(onSaleType===0){
        return originTotal-Math.floor(originTotal/300)*50
    }
}

getActualTotal(1,2680); //2208

上面這段代碼很簡單,並且缺點也很明顯。隨着個人滿減策略逐漸增多,getActualTotal函數會越變越大,並且充滿了if判斷,稍一疏忽就容易弄錯。架構

OK,有人說我很懶,雖然這樣不夠優雅但並不影響個人使用,畢竟滿減策略再多也多不到哪去。
我只能說,需求永遠不是程序員定的。。這時,市場人員說咱們新版程序添加了會員功能,咱們須要支持如下的促銷策略:函數

會員促銷策略:
  1. 會員充300返60,且首單打9折
  2. 會員充500返100,且首單打8折
  3. 會員充1000返300,且首單打7折
  4. ...

這個時候,若是你還在原先的getActualTotal函數中繼續添加if判斷,我想若是你的領導review你這段代碼,可能會懷疑本身當初怎麼把你招進來。。學習

OK,咱們終於下定決心要重構促銷策略的代碼,咱們能夠這麼作:spa

var vipPolicy_0=function(originTotal){
    return originTotal-Math.floor(originTotal/100)*10
}
var vipPolicy_1=function(originTotal){
    return originTotal-Math.floor(originTotal/200)*30
}
...
//會員充1000返300
var vipPolicy_10=function(account,originTotal){
    if(account===0){
        account+=1300;
        return originTotal*0.9
    }else{
        account+=1300;
        return originTotal;
    }
    return originTotal-Math.floor(originTotal/200)*30
}
...
var vipPolicy_n=function(){
    ...
}

var getActualTotal=function(onSaleType,originTotal,account){
    switch(onSaleType){
        case 0:
            return vipPolicy_0(originTotal);
        case 1:
            return vipPolicy_0(originTotal);
        ...
        case n:
            return ...
        default:
            return originTotal;
    }
}

好了,如今咱們每種策略都有本身獨立的空間了,看起來層次分明。可是還有兩個問題沒有解決:

  1. 隨着促銷策略的增長,getActualTotal的代碼量依然會愈來愈大
  2. 系統缺少彈性,若是須要增長一種策略,那麼除了添加一個策略函數,還須要修改switch...case..語句

讓咱們再來回顧一下策略模式的定義:

定義一系列的算法,把它們一個個封裝起來,而且使它們可互相替換

在咱們的例子中,每種促銷策略的實現方式是不同的,但咱們最終的目的都是爲了求得實際金額。策略模式能夠把咱們對促銷策略的算法一個個封裝起來,而且使它們可互相替換而不影響咱們對實際金額的求值,這正好是咱們所須要的。

下面咱們用策略模式來重構上面的代碼:

var policies={
    "Type_0":function(originTotal){
        return originTotal-Math.floor(originTotal/100)*10 
    },
    "Type_1":function(originTotal){
        return originTotal-Math.floor(originTotal/200)*30 
    },
    ...
    "Type_n":function(originTotal){
        ... 
    }
}

var getActualTotal=function(onSaleType,originTotal,account){
    return policies["Type_"+onSaleType](originTotal,account)
}
//執行
getActualTotal(0,2680.00);//2208

分析上面的代碼咱們發現,無論促銷策略如何增長,getActualTotal函數徹底不須要再變化了。咱們要作的,就是增長新策略的函數而已。

經過策略模式的代碼,咱們消除了讓人反胃的大片條件分支語句,getActualTotal自己並無計算能力,而是將計算全權委託給了策略函數。

由此咱們能夠總結出策略模式實現的要點:

  1. 將變化的算法封裝成獨立的策略函數,並負責具體的計算
  2. 委託函數,該函數接受客戶請求,並將請求委託給某一個具體的策略函數

用一張UML圖表示以下:
圖片描述

怎麼樣?如今看到上面這張圖是否是有了瞭然於胸的感受?那就趕忙去試一試策略模式吧!


參考書籍:

  1. 《設計模式:可複用面向對象軟件的基礎》
  2. 《大話設計模式》
  3. 《Javascript設計模式與開發實踐》
相關文章
相關標籤/搜索