閱讀目錄

定義

策略模式
官方定義:定義了一系列的算法,并將每一個(gè)算法封裝起來(lái),而且使它們還可以相互替換。
個(gè)人理解:選擇執(zhí)行多個(gè)規(guī)則中的某個(gè)規(guī)則。

C#實(shí)現(xiàn)

需求1: 開(kāi)發(fā)一個(gè)商場(chǎng)收銀系統(tǒng)v1.0

三下五除二搞定
seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營(yíng)銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營(yíng)銷培訓(xùn)
代碼實(shí)現(xiàn):

var price = Convert.ToDouble(txtPrice.Text);//單價(jià)var number = Convert.ToDouble(txtNumber.Text);//數(shù)量var lastTotal = Convert.ToDouble(labTotal.Text);//已購(gòu)買金額var money = price * number;//本次計(jì)算金額labTotal.Text = (lastTotal + money).ToString();
txtContent.Text += string.Format("單價(jià):{0},數(shù)量:{1},金額:{2}", price, number, money + "\r\n");

系統(tǒng)簡(jiǎn)單、方便、實(shí)用、性能好。...幾個(gè)月過(guò)去,馬上要國(guó)慶長(zhǎng)假,老板為了促銷決定全場(chǎng)8.8折。

需求2: 加入打折功能
因?yàn)轫?xiàng)目工期緊,三下五除二又搞定

var price = Convert.ToDouble(txtPrice.Text);//單價(jià)var number = Convert.ToDouble(txtNumber.Text);//數(shù)量var lastTotal = Convert.ToDouble(labTotal.Text);//已購(gòu)買金額var discount = 0.88;//折扣(新增代碼)var money = price * number * discount;//本次計(jì)算金額labTotal.Text = (lastTotal + money).ToString();
txtContent.Text += string.Format("單價(jià):{0},數(shù)量:{1},折扣:{2},實(shí)際金額:{2}", price, number, discount, money + "\r\n");

很是自豪,我開(kāi)發(fā)效率就是這么高。老板也甚是高興。
...轉(zhuǎn)眼假期就要過(guò)去了,打折的活動(dòng)也要取消了。但是,由于這次的促銷效果收益還不錯(cuò)。老板決定繼續(xù)打折活動(dòng),折扣率要成為9.8折,且只是部分商品。不打折的商品則實(shí)行滿300返40,滿600返100。
不對(duì)勁啊,又的改代碼。到了明年是不是又要8.8?老板的心思猜不透,但程序可以寫得更靈活。所以我們要好好構(gòu)思下。讓系統(tǒng)可以選擇優(yōu)惠策略。

需求3: 修改打折,并加入返現(xiàn)功能
seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營(yíng)銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營(yíng)銷培訓(xùn)
代碼實(shí)現(xiàn)如下

var price = Convert.ToDouble(txtPrice.Text);//單價(jià)var number = Convert.ToDouble(txtNumber.Text);//數(shù)量var lastTotal = Convert.ToDouble(labTotal.Text);//已購(gòu)買金額            var money = price * number;//本次計(jì)算金額switch (cmBstrategy.Text)//下拉框{    case "8.8折":
        money *= 0.88;        break;    case "9.8折":
        money *= 0.98;        break;    case "滿300返40":        if (money >= 300)
        {
            money -= 40;
        }        break;    case "滿600返100":        if (money >= 600)
        {
            money -= 100;
        }        break;
}

labTotal.Text = (lastTotal + money).ToString();
txtContent.Text += string.Format("單價(jià):{0},數(shù)量:{1},促銷:{2},實(shí)際金額:{3}", price, number, cmBstrategy.Text, money + "\r\n");

現(xiàn)在我們的收銀員可以靈活切換優(yōu)惠活動(dòng),且保留的原有的優(yōu)惠策略。不過(guò)我們從代碼層面考慮的話,還有多處不足。

  • switch條件分支語(yǔ)句難以閱讀和維護(hù)。

  • 如果我們需要修改新增優(yōu)惠策略的話,需在界面代碼里面修改。

根據(jù)面向?qū)ο蟮乃枷?,?yīng)該封裝變化。于是,我們的策略模式可以登場(chǎng)了。

代碼重構(gòu) 使用策略模式實(shí)現(xiàn)以上需求

var price = Convert.ToDouble(txtPrice.Text);//單價(jià)var number = Convert.ToDouble(txtNumber.Text);//數(shù)量var lastTotal = Convert.ToDouble(labTotal.Text);//已購(gòu)買金額      
 var context = new Context(cmBstrategy.Text);//新增代碼var money = context.Calculation(price, number);//新增代碼labTotal.Text = (lastTotal + money).ToString();
txtContent.Text += string.Format("單價(jià):{0},數(shù)量:{1},促銷:{2},實(shí)際金額:{3}", price, number, cmBstrategy.Text, money + "\r\n");

我們發(fā)現(xiàn)中間那段條件分支不見(jiàn)了,多了一個(gè)Context類。

public class Context{    //策略抽象類
    private AmountCalculation amountCalculation;    public Context(string type)    {        switch (type)
        {            case "8.8折":
                amountCalculation = new Rebate(0.88);                break;            case "9.8折":
                amountCalculation = new Rebate(0.98);                break;            case "滿300返40":
                amountCalculation = new Cashback(300, 40);                break;            case "滿600返100":
                amountCalculation = new Cashback(600, 100);                break;
        }
    }    //計(jì)算金額
    public double Calculation(double price, double number)    {        return amountCalculation.Calculation(price, number);
    }
}

里面有類Rebate折扣計(jì)算、Cashback返現(xiàn)計(jì)算。

//折扣計(jì)算public class Rebate : AmountCalculation{    private double discountRate;    public Rebate(double discountRate)    {        this.discountRate = discountRate;
    }    public override double Calculation(double price, double number)    {        return price * number * discountRate;
    }
}
// 返現(xiàn)public class Cashback : AmountCalculation{    //滿多少
    private double exceed;    //返多少
    private double retreat;    public Cashback(double exceed, double retreat)    {        this.exceed = exceed;        this.retreat = retreat;
    }    public override double Calculation(double price, double number)    {        var momoney = price * number;        if (momoney >= exceed)
        {            return momoney - retreat;
        }        return momoney;
    }
}

看到這里,是不是明白了策略模式呢?
如果現(xiàn)在老板再需要我們價(jià)格折扣或是返現(xiàn)的活動(dòng),相比之前需要在長(zhǎng)段的界面邏輯代碼里面修改,現(xiàn)在要方便得多了。
第一、先在界面添加一個(gè)活動(dòng)如加一個(gè)7.8折,然后界面代碼就不用動(dòng)了。
第二、在Context類里面加一個(gè)7.8折

switch (type)
{    //新增
    case "7.8折":
        amountCalculation = new Rebate(0.78);        break;

JS實(shí)現(xiàn)

上面用C#實(shí)現(xiàn)了策略模式,接下來(lái)我們嘗試使用js來(lái)實(shí)現(xiàn)。還是借用用上面的商場(chǎng)活動(dòng)業(yè)務(wù)。
js不同于傳統(tǒng)的面向?qū)ο螅瑹o(wú)類、不需要實(shí)現(xiàn)抽象類。

//策略計(jì)算var strategies = {    //返現(xiàn)  exceed:滿多少  retreat:返多少  momoney:應(yīng)付金額
    cashBack: function (exceed, retreat, momoney) {        if (momoney >= exceed) {            return (momoney - retreat).toFixed(2);
        }        return momoney;//返現(xiàn)后實(shí)付金額
    },    //打折 discountRate:折扣率  momoney:應(yīng)付金額
    rebate: function (discountRate, momoney) {        return (discountRate * momoney).toFixed(2);//折扣后實(shí)付金額
    }
}
//上下文var context = {    "7.8折": function (price, number) {        var momoney = price * number;        return strategies.rebate(0.78, momoney);
    },    "9.8折": function (price, number) {        var momoney = price * number;        return strategies.rebate(0.98, momoney);
    },    "滿300返40": function (price, number) {        var momoney = price * number;        return strategies.cashBack(300, 40, momoney);
    },    "滿600返100": function (price, number) {        var momoney = price * number;        return strategies.cashBack(600, 100, momoney);
    }
}
//計(jì)算結(jié)果var calculateBonus = function (level, price, number) {    return context[level](price, number);
};
//調(diào)用console.log(calculateBonus('7.8折', 12, 3));//計(jì)算console.log(calculateBonus('滿600返100', 12, 3));//計(jì)算console.log(calculateBonus('滿300返40', 2, 23));//計(jì)算console.log(calculateBonus('9.8折', 2, 33));//計(jì)算

結(jié)果如下:
seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營(yíng)銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營(yíng)銷培訓(xùn)
相對(duì)于面向?qū)ο笳Z(yǔ)言的實(shí)現(xiàn)要更加的清晰明了。
那么js可以模擬面向?qū)ο蟮膶?shí)現(xiàn)嗎?答案是肯定的。
首先定義返現(xiàn)實(shí)現(xiàn)類:

//返現(xiàn)  exceed:滿多少  retreat:返多少var CashBack = function (exceed, retreat) {    this.exceed = exceed;    this.retreat = retreat;
};//計(jì)算方法CashBack.prototype.calculate = function (price, number) {    var momoney = price * number;    if (momoney >= this.exceed) {        return (momoney - this.retreat).toFixed(2);
    }    return momoney;//返現(xiàn)后實(shí)付金額}

打折類

//打折 discountRate:折扣率  momoney:應(yīng)付金額var Rebate = function (discountRate) {    this.discountRate = discountRate;
};//計(jì)算方法Rebate.prototype.calculate = function (price, number) {    return (price * number * this.discountRate).toFixed(2);//折扣后實(shí)付金額}

策略上下文

//上下文var Context = function (type) {    this.type = type; 
}
Context.prototype.calculation = function (price, number) {    var AmountCalculation;    switch (this.type) {        case "7.8折":
            AmountCalculation = new Rebate(0.78);            break;        case "9.8折":
            AmountCalculation = new Rebate(0.98);            break;        case "滿300返40":
            AmountCalculation = new CashBack(300, 40);            break;        case "滿600返100":
            AmountCalculation = new CashBack(600, 100);            break;
    }    return AmountCalculation.calculate(price, number);
}

調(diào)用如下:

//調(diào)用var context = new Context("7.8折");  
console.log(context.calculation(12, 3));var context = new Context("9.8折");  
console.log(context.calculation(12, 3));//計(jì)算var context = new Context("滿300返40");  
console.log(context.calculation(300, 2));//計(jì)算var context = new Context("滿600返100");  
console.log(context.calculation(300, 3));//計(jì)算

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營(yíng)銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營(yíng)銷培訓(xùn)
雖然對(duì)于js語(yǔ)言特性直接實(shí)現(xiàn)策略模式來(lái)說(shuō),面向?qū)ο蟮拇a量比較多??墒菍?duì)于我們后端人員new一個(gè)對(duì)象的使用方式應(yīng)該更習(xí)慣。
 

策略模式場(chǎng)景

  • 排序算法的選擇冒泡排序、選擇排序、快速排序、插入排序

  • 壓縮算法的選擇zip、rar、rar5...

  • 旅游交通工具的選擇飛機(jī)、火車、汽車...
     

總結(jié)
策略模式通過(guò)Context上下文對(duì)具體策略進(jìn)行封裝,供高層直接調(diào)用而不用關(guān)系策略的具體實(shí)現(xiàn)。然后Context本身通過(guò)不同情況實(shí)例不同的抽象實(shí)現(xiàn)類(具體策略類),來(lái)執(zhí)行具體策略。從而實(shí)現(xiàn)了具體策略的自由切換,易于新策略的擴(kuò)展。
本文已同步至索引目錄:《設(shè)計(jì)模式學(xué)習(xí)》
本文demo:https://github.com/zhaopeiym/BlogDemoCode

  • 學(xué)習(xí)本是一個(gè)不斷抄襲、模仿、練習(xí)、創(chuàng)新的過(guò)程。

  • 雖然,園中已有本人無(wú)法超越的同主題博文,為什么還是要寫。

  • 對(duì)于自己,博文只是總結(jié)。在總結(jié)的過(guò)程發(fā)現(xiàn)問(wèn)題,解決問(wèn)題。

  • 對(duì)于他人,在此過(guò)程如果還能附帶幫助他人,那就再好不過(guò)了。

  • 由于博主能力有限,文中可能存在描述不正確,歡迎指正、補(bǔ)充!

  • 感謝您的閱讀。如果文章對(duì)您有用,那么請(qǐng)輕輕點(diǎn)個(gè)贊,以資鼓勵(lì)。

分類: 學(xué)習(xí)區(qū)2.3【設(shè)計(jì)模式】