策略模式

模擬場景:

某某公司要求我們做一個商場收銀系統,

提出需求:商場會不定時舉辦一系列的優惠活動,優惠方式暫定爲:打折扣,滿多少還多少(例如:滿300還100)

 

初步場景分析:

看到這個需求,第一感覺就會潛意識的認爲“這個太簡單了”。

1.商場收銀系統:定義爲winform的應用程序

2.活動優惠的計算,判斷一下就可以了。

 

初步代碼實現:

 

  1. /// <summary>  
  2.        /// 點擊確定計算收費  
  3.        /// </summary>  
  4.        /// <param name="sender"></param>  
  5.        /// <param name="e"></param>  
  6.        private void btnOk_Click(object sender, EventArgs e)  
  7.        {  
  8.            int number = Convert.ToInt32(this.txtNumber.Text);  
  9.            double price = Convert.ToDouble(this.txtPrice.Text);  
  10.            double total = 0;  
  11.            switch (this.cbxType.SelectedItem.ToString())  
  12.            {  
  13.                case "正常收費":  
  14.                    total = number * price;  
  15.                    break;  
  16.                case "滿300返100":  
  17.                    moneyCondition = 300;  
  18.                    moneyReturn = 100;  
  19.                    double money = Convert.ToDouble(txtPrice.Text) * Convert.ToInt32(txtNumber.Text);  
  20.                    if (money >= moneyCondition)  
  21.                    {  
  22.                        total = money - Math.Floor(money / moneyCondition) * moneyReturn;  
  23.                    }  
  24.                    break;  
  25.                case "打8折":  
  26.                    total = Convert.ToDouble(txtPrice.Text) * Convert.ToInt32(txtNumber.Text) * 0.8;  
  27.                    break;  
  28.            }  
  29.            this.lbxList.Items.Add("單價:" + txtPrice.Text + "數量:" + txtNumber.Text + " "  
  30.                + cbxType.SelectedItem.ToString() + "合計: " + total.ToString());  
  31.            this.lblResult.Text = total.ToString();  
  32.   
  33.        }  


好,現在我們來分析一下上面的實現:

咋一看,感覺沒什麼問題,可再細分析,問題就多了:

1:顯示和邏輯緊密的聯繫在一起。

2:完全過程式的編程,沒辦法複用。

3:當新需求增加的時候,還需要修改這個條件分支,不符合開放封閉原則,過多的判斷不利於維護。

經過以上分析,進行初步的修改:

先來看看結構圖:

 

工廠類:

  1. public class CashFactory  
  2.     {  
  3.         public static CashSuper createCashAccpet(string type)  
  4.         {  
  5.             CashSuper cs = null;  
  6.             switch (type)  
  7.             {  
  8.                 case "正常收費":  
  9.                     cs = new CashNormal();  
  10.                     break;  
  11.                 case "滿300返100":  
  12.                     cs = new CashReturn("300","100");  
  13.                     break;  
  14.                 case "打8折":  
  15.                     cs = new CashRebate("0.8");  
  16.                     break;  
  17.             }  
  18.             return cs;  
  19.         }  
  20.     }  

運算父類:

  1. /// <summary>  
  2.     /// 現金收取父類,算法的抽象類  
  3.     /// </summary>  
  4.     public abstract class CashSuper  
  5.     {  
  6.         /// <summary>  
  7.         /// 抽象方法:收取現金  
  8.         /// </summary>  
  9.         /// <param name="money">原價</param>  
  10.         /// <returns>當前價</returns>  
  11.         public abstract double acceptCash(double money);  
  12.     }  


折扣類:

  1.  /// <summary>  
  2. /// 打折收費  
  3. /// </summary>  
  4. public class CashRebate:CashSuper  
  5. {  
  6.     private double moneyRebate = 1d;  
  7.   
  8.     public CashRebate(string money)  
  9.     {  
  10.         this.moneyRebate = double.Parse(money);  
  11.     }  
  12.   
  13.     //初始化時必需輸入折扣率,如八折就是0.8  
  14.     public override double acceptCash(double money)  
  15.     {  
  16.         return money * moneyRebate;  
  17.     }  
  18. }  


返利類:

  1. /// <summary>  
  2. /// 返利收費  
  3. /// </summary>  
  4. public class CashReturn:CashSuper  
  5. {  
  6.     private double moneyCondition = 0.0d;  
  7.     private double moneyReturn = 0.0d;  
  8.   
  9.     /// <summary>  
  10.     /// 初始化時必須要輸入返利條件和返利值,比如滿300返100,則moneyCondition爲300,moneyReturn爲100  
  11.     /// </summary>  
  12.     /// <param name="moneyCondition"></param>  
  13.     /// <param name="moneyReturn"></param>  
  14.     public CashReturn(string moneyCondition,string moneyReturn)  
  15.     {  
  16.         this.moneyCondition = double.Parse(moneyCondition);  
  17.         this.moneyReturn = double.Parse(moneyReturn);  
  18.     }  
  19.   
  20.     public override double acceptCash(double money)  
  21.     {  
  22.         double result = money;  
  23.         //若大於返利條件,則需要減去返利值  
  24.         if (money>=moneyCondition)  
  25.         {  
  26.             result = money-Math.Floor(money/moneyCondition)*moneyReturn;  
  27.         }  
  28.         return result;  
  29.     }  
  30. }  


正常收費類:

  1.  /// <summary>  
  2. /// 正常收費  
  3. /// </summary>  
  4. public class CashNormal:CashSuper  
  5. {  
  6.     public override double acceptCash(double money)  
  7.     {  
  8.         return money;  
  9.     }  
  10. }  


好的,經過完善之後我們的代碼已經出來了,下面我們再來細細分析一下這代碼:

代碼使用了簡單工廠模式來解決了對象的創建,通過抽象實現了業務邏輯的分離,在維護性上也得到了改善。

那麼這代碼就沒有問題了嗎?答案是:有

現在假設我們要爲商場添加一種優惠活動,滿100積分送10點,以後積分達到一定值就可以領取獎品。

我們該怎麼去做呢:現在有了工廠,我們只需要添加一個積分的算法類,

讓它繼承運算父類(CashSuper),在再工廠條件分支裏添加一個滿100積分送10點的條件分支,界面再稍稍修改就可以了。

這麼想雖然是可以解決問題,但是工廠裏包含了所有的收費方式,商場是可能經常性的更改打折額度,添加新的優惠方式,

如果是這樣的話,我們每次維護或擴展收費方式都要去改動這個工廠,這樣做就不得不讓代碼重新編譯部署,這樣的處理方式是很糟糕的。

那麼我們該如何去做呢?現在我們就進入正題:其實商場的促銷活動,打折活動,返利活動都是一些算法,而算法本身只是一種策略,

最重要的是這些算法是隨時都可能互相替換的,這就是變化點,而封裝變化點是我們面向對象的一種很重要的思維方式。
 

 

我們先來看看策略模式的結構圖:

Strategy類,定義所有支持的算法和公共接口:

ConcreteStrategy封裝了具體的算法或行爲,繼承於Strategy:

Context,用一個ConcreteStrategy來配置,維護一個對Strategy對象的引用:

客戶端的實現:

 

下面我們就用策略模式來改善一下我們的代碼:

代碼結構圖:

現有的代碼沒有CashContext,我們就添加一個CashContext的類:

其他的類不變,客戶端實現:

經過策略模式完善的代碼已經實現,下面我們再來分析一下我們的代碼:

現在問題又來了,算法的判斷又回到客戶端裏來了,我們該如何去解決這個問題呢?

其實我們可以讓策略模式與簡單工廠模式相結合,下面我們來看看怎麼個實現法:

在CashContext裏進行對象的構造:

客戶端實現:

 

經過進一步的優化,我們的代碼已經比較的優化了,但是一個很明顯的問題也顯示出來了,就是switch的條件判斷分支,

當我們需要添加一種算法的時候,我們還是需要修改CashContext裏的switch算法,這樣的代碼還是讓人非常的不爽,哪還有什麼方法呢?

我們可以使用反射來改善現有的代碼(反射的具體原理與實現將在不久寫出):

switch算法的條件判斷分支我們抽取出來,用一個xml文件進行記錄:

接下來我們只需要在加載事件中將xml的信息讀取出來綁定就可以了:

由於我們的switch已經轉移到xml中了,那麼我們的CashContext類就可以簡化成:

我們的具體算法實現類不變,現在我們最主要就是通過反射去實例化不同的算法對象:

 

 總結:

代碼經過多次的修改,可維護性,代耦合高內聚的特性已經展現出來了,現在我們再來分析一下上面的代碼吧,上面我們通過反射去除了switch分支,

有效的把耦合降到最低,但是這樣做也是有一點點代價的,反射會比較耗一點性能,所以我們做程序的時候要具體問題具體分析。

通過上面的代碼我們來分析一下策略模式吧:

策略模式是一種定義一系列算法的方法,從概念上來看,所有這些算法完成的都是相同的工作,只是實現不同,它可以以相同的方式調用所有的算法,減少了各種算法類與使用算法類之間的耦合。

 

應用場景:
1、 多個類只區別在表現行爲不同,可以使用Strategy模式,在運行時動態選擇具體要執行的行爲。
2、 需要在不同情況下使用不同的策略(算法),或者策略還可能在未來用其它方式來實現。
3、 對客戶隱藏具體策略(算法)的實現細節,彼此完全獨立。
 

優點:

1、策略模式簡化了單元測試,因爲每個算法都有自己的類,可以通過自己的接口單獨測試,每個算法可以保證它沒有錯誤,修改其中任一個時也不會影響其他的算法。

2、策略模式封裝了變化,策略模式就是用來封裝算法的,但在實踐中,我們發現可以用它來封裝幾乎任何類型的規則,
只要在分析過程中聽到需要在不同時間應用不同的業務規則,就可以考慮使用策略模式處理這種變化的可能性。

3、策略模式有利於程序的高內聚、低偶合性,使得程序更加的靈活。

 

缺點:

基本策略模式中所用具體實現的職責由客戶端對象承擔,並轉給策略模式的Context對象,
這本身並沒有解除客戶端需要選擇判斷的壓力,而策略模式與簡單工廠模式結合後,選擇具體實現的職責也可以由Context來承擔,這就是最大化地減輕了客戶端的職責。


以上內容轉自(盡供本人學習使用):http://blog.csdn.net/shiyuan17/article/details/9056637 。感謝博主花費時間和經歷爲我們準備如此好的學習資料。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章