天天看點

設計模式那點事政策模式

  •   應用場景  

  現在我們要做一個商場收銀軟體,營業員根據所購買商品的單價和數量,向客戶收取費用,如果是你,你會怎麼設計?

  •   閱讀目錄

    一:大部分人的寫法v1.0

  二:第一次改版後的代碼v1.1

  三:第二次改版後的代碼v1.2

  四:第三次改版後的代碼v1.3

  六:第四次改版後的代碼v1.4

  七:對比v1.2版的簡單工廠模式和v1.4版的政策模式在用戶端的代碼對比

  八:政策模式思考

  一:大部分人的寫法v1.0

  用兩個文本框來輸入單價和數量,一個确定按鈕來計算出每種商品的費用,用個清單框來記錄購買的商品清單,一個标簽來記錄總價,一個重置按鈕來全部清空以便重新開始

  1、Question?

  這樣的寫法會帶來一個問題?什麼問題呢?折扣率不靈活性的問題,好的問題來了?什麼問題呢?那商場現在搞活動,全場商品一律8折,好,那現在商場又不搞活動了

  2、Answer

  全場商品一律8折,那不是就在douTotalPrice後面乘以0.8不就完事了麼,商場又不搞活動了,改回來,然後再用改後的程式把客戶的機器重新部署一遍

 回答錯誤

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Strategy
{
   public partial class Form1 : Form
   {
    public Form1()
    {
      InitializeComponent();
    }

      /// <summary>
      /// 确定
      /// </summary>
      double douTotal = 0.0d;//總價
      private void btnOK_Click(object sender, EventArgs e)
      {
         double douTotalPrice = Convert.ToDouble(this.txtPrice.Text) * Convert.ToInt32(this.txtNum.Text);//合計

      //将每個商品的合計計入總計
          douTotal = douTotal + douTotalPrice;

      //清單框中顯示購買商品的資訊
          this.lvwProduct.Items.Add("單價:" + this.txtPrice.Text + " 數量:" + this.txtNum.Text + " 合計:" + douTotalPrice.ToString());
          this.lblResult.Text = douTotal.ToString();
       }

    /// <summary>
    /// 重置
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnReset_Click(object sender, EventArgs e)
    {
      this.txtPrice.Text = "";
      this.txtNum.Text = "";
      this.lvwProduct.Items.Clear();
      this.lblResult.Text = "0.00";
    }
  }
}      

   二:第一次改版後的代碼v1.1

  2.1、為什麼要改版?

  因為考慮到折扣率是容易變化的,v1.0不具備靈活性,要具備靈活性

  2.2、如何改版?

    做一個下拉清單框,把商場所能考慮的折扣率都寫進去,這樣需要變化的可能性就會小很多了,就能應付變化的折扣率了

  這次靈活性上比v1.0那版代碼好多了,不過重複的代碼也挺多的,比如:Convert.ToDouble(),這裡就寫了5次,而5個分支除了打折多少外不同,幾乎沒什麼差別,應該考慮重構一下

  面向對象程式設計不是類越多越好,類的劃分是為了封裝,但是分類的基礎是抽象,具有相同屬性和功能的對象的抽象集合才是類,“打八折”和“打七折”隻是形式不同,抽象分析出來,所有的打折算法是一樣的,是以打折算法應該是一個類

  2.3、New Question?

  現在商場活動加大了,需要有滿300返100的促銷算法,又該怎們辦?既然滿300返100,那麼滿700就要返200了,萬一以後,商場推出其他促銷活動,滿100積10分,那麼滿200就要積20分了,積分以後可以換商品,他奶奶的,又過了一陣子,商場又推出了另外一個促銷手段,滿1000送手機,滿2000送電腦,等等類似的無數個促銷手段,想想我們到底應該怎麼辦?怎麼才能解決這種可能的無限促銷手段的算法?

   2.3.1:打折促銷手段

  比如:打5折,打8折

  2.3.2:返利促銷手段

  比如:滿300返100,那麼滿700就返200

  2.3.3:送積分促銷手段

  比如:滿100積10分,那麼滿200就積20分

  2.3.4:抽獎促銷手段

  比如:滿1000可以參加抽獎

  2.3.5:送實物促銷手段

  比如:滿500送天翼手機,滿800送微波爐

  2.4、Answer

  有人這樣說了,應對這種情況應當首先寫一個父類,其次再繼承它實作打折的子類和返利的子類以及得積分的子類等等促銷手段的子類,利用多态性完成,那麼這裡很明顯就該用到我們上章講的《邁向架構設計師之路系列—1-簡單對象通路模式》一文的思想了,那麼應該寫幾個子類呢?就目前商場的活動來說,“打八折”一個子類,“打七折”一個子類,“打五折”一個子類,“打三折”一個子類,“滿300送100”一個子類,“滿200送50”一個子類,這都要寫6個子類,那麼現在商場要“打一折”,“滿500送200”,難道還要再去增加子類嗎?有必要這樣嗎?以後隻要商場的折扣率和返利一變,有你受的了,你就無限的增加子類吧,想想看這之中哪些是相同的?,哪些是不同的?,對了,打折都是一個行為,隻是折扣率不同罷了,“打八折”和“打七折”隻是形式不同,抽象分析出來,所有的打折算法是一樣的,是以打折算法應該是一個類,返利也隻是一個行為,隻是返利不同罷了,就打折來說隻要有個初始化參數就行了,返利則需要兩個初始化參數,滿300送100”和“滿200送50”隻是形式不同,抽象分析出來,所有的返利算法是一樣的,是以返利算法應該是一個類

回答正确

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Strategy
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }  

    /// <summary>
    /// 加載
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Form1_Load(object sender, EventArgs e)
    {
      this.cboType.Items.AddRange(new object[]{"正常收費","打八折","打七折","打五折","打三折"});
      this.cboType.SelectedIndex = 0;
    }

    /// <summary>
    /// 确定
    /// </summary>
    double douTotal = 0.0d;//總價
    private void btnOK_Click(object sender, EventArgs e)
    {
      double douTotalPrice = 0d;//合計
      switch (this.cboType.SelectedIndex)
      {
        //正常收費
        case 0:
          douTotalPrice = Convert.ToDouble(this.txtPrice.Text) * Convert.ToInt32(this.txtNum.Text);
          break;
        //打八折
        case 1:
          douTotalPrice = Convert.ToDouble(this.txtPrice.Text) * Convert.ToInt32(this.txtNum.Text) * 0.8;
          break;
        //打七折
        case 2:
          douTotalPrice = Convert.ToDouble(this.txtPrice.Text) * Convert.ToInt32(this.txtNum.Text) * 0.7;
          break;
        //打五折
        case 3:
          douTotalPrice = Convert.ToDouble(this.txtPrice.Text) * Convert.ToInt32(this.txtNum.Text) * 0.5;
          break;
        //打三折
        case 4:
          douTotalPrice = Convert.ToDouble(this.txtPrice.Text) * Convert.ToInt32(this.txtNum.Text) * 0.3;
          break;
      }
      douTotal = douTotal + douTotalPrice;
      this.lvwProduct.Items.Add("單價:" + this.txtPrice.Text + " 數量:" + this.txtNum.Text + " 合計:" + douTotalPrice.ToString());
      this.lblResult.Text = douTotal.ToString();
    }

    /// <summary>
    /// 重置
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnReset_Click(object sender, EventArgs e)
    {
      this.txtPrice.Text = "";
      this.txtNum.Text = "";
      this.lvwProduct.Items.Clear();
      this.lblResult.Text = "0.00";
    }
  }
}      

  

  三:第二次改版後的代碼v1.2

  3.1、為什麼又要改版?

  因為考慮到促銷的手段是容易變化的,v1.1不具備靈活性,要具備靈活性

  3.2、UML類圖結構

  我們先解析一下v1.2版的UML類圖來看,抽象類用斜體表示,繼承關系用空心三角形+實作表示,依賴關系用虛線箭頭來表示

  

  具體到我們現在的執行個體上

  

  3.3、New Question?

  對于v1.2版的代碼,我們需要增加“打一折”和“滿700返300”的促銷活動,我們該怎麼辦?我們隻要在收費對象生成工廠中加兩個分支,并且在界面的下拉清單框中加兩個選項就搞定了,那如果我們增加一種全新的促銷手段,“滿100返10積分”積分到一定程度可以兌換禮品,我們又該怎麼辦?加一個積分算法類繼承自CashSuper類,積分算法類構造函數接受兩個參數,一個是條件一個是返點,再到收費對象生成工廠裡面加個,“滿100返10積分”的分支就行了,簡單工廠模式雖然也能解決這個問題,但是第一個方面:這個模式隻是解決對象的建立問題,由于簡單工廠本身包含了所有的收費方式,我想每一個商場一定會經常變動打折折扣率和返利額度的以及變更促銷手段的,每次維護折扣率和額度以及擴充促銷手段都要改動這個收費對象生成工廠,以至于代碼要重新編譯和部署,這是很糟糕的處理方式,是以用簡單對象通路模式不是最好的辦法,想想還有其他的方法嗎?第二個方面:面對算法的經常變動會應該有更好的解決辦法的,是什麼呢?大家想想?

  3.4、Answer

   面對算法的經常變動會應該有更好的解決辦法的,是什麼呢?沒錯是政策模式(Strategy)

  政策模式(Strategy):它定義了算法家族,分别封裝起來,讓它們之間可以互相替換,此模式讓算法的變化,不會影響到使用算法的客戶

  回答正确

//1:AbstractClass.CashSuper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AbstractClass
{   
     /// <summary>
     /// 現金收取抽象類
    /// </summary>
    public abstract class CashSuper
    {
        /// <summary>
        /// 現金收取抽象類的收取現金抽象方法,參數為原價,傳回值為目前價
        /// </summary>
        /// <param name="douMoney">原價</param>
        /// <returns>目前價</returns>
        public abstract double AccpetCash(double douMoney);
    }
}
//2:BusinessLogic.CashNormal.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AbstractClass;
namespace BusinessLogic
{
    /// <summary>
    /// 正常收費子類
    /// </summary>
    public class CashNormal:CashSuper
    {
        /// <summary>
        /// 正常收費方法
        /// </summary>
        /// <param name="douMoney">原價</param>
        /// <returns>原價</returns>
        public override double AccpetCash(double douMoney)
        {
            return douMoney;
        }
    }
}
//3:BusinessLogic.CashRebate.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AbstractClass;
namespace BusinessLogic
{
    /// <summary>
    /// 打折收費子類
    /// </summary>
    public class CashRebate:CashSuper
    {
        private double douMoneyRebate;//折扣率
        /// <summary>
        /// 構造函數初始化
        /// </summary>
        /// <param name="douMoney">折扣率</param>
        public CashRebate(double douMoneyRebate)
        {
            this.douMoneyRebate = douMoneyRebate;
        }  
        /// <summary>
        /// 打折收費方法
        /// </summary>
        /// <param name="douMoney">原價</param>
        /// <returns>折扣價</returns>
        public override double AccpetCash(double douMoney)
        {
            return douMoney * this.douMoneyRebate;
        }
    }
}
//4:BusinessLogic.CashReturn.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AbstractClass;
namespace BusinessLogic
{
    /// <summary>
    /// 返利收費子類
    /// </summary>
    public class CashReturn:CashSuper
    {
        private double douMoneyCondition = 0.0d;//返利條件
        private double douMoneyReturn = 0.0d;//返利值
        /// <summary>
        /// 構造函數初始化
        /// </summary>
        /// <param name="douMoneyCondition">返利條件</param>
        /// <param name="douMoneyReturn">返利值</param>
        public CashReturn(double douMoneyCondition, double douMoneyReturn)
        {
            this.douMoneyCondition = douMoneyCondition;
            this.douMoneyReturn = douMoneyReturn;
        }

        /// <summary>
        /// 返利收費方法
        /// </summary>
        /// <param name="douMoney">原價</param>
        /// <returns>返利價</returns>
        public override double AccpetCash(double douMoney)
        {
            double douResult = douMoney;
            //若大于返利條件,則需要減去返利值
            if (douMoney >= douMoneyCondition)
              douResult = douMoney - Math.Floor(douMoney / douMoneyCondition) * douMoneyReturn;
            return douResult;
        }
    }
}
//5:CashFactory.CashFactory.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AbstractClass;
using BusinessLogic;
namespace CashFactory
{
    /// <summary>
    /// 收費對象生成工廠類
    /// </summary>
    public class CashFactory
    {
        static CashSuper cashsuper = null;
        /// <summary>
        /// 簡單工廠
        /// </summary>
        /// <param name="strType">類型</param>
        /// <returns>現金收取抽象類</returns>
        public static CashSuper CreateCashAccpet(string strType)
        {
            switch (strType)
            { 
              case "正常收費":
                  cashsuper = new CashNormal();
                  break;
              case "打八折":
                  cashsuper = new CashRebate(0.8);
                  break;
              case "打七折":
                  cashsuper = new CashRebate(0.7);
                  break;
              case "打五折":
                  cashsuper = new CashRebate(0.5);
                  break;
              case "打三折":
                  cashsuper = new CashRebate(0.3);
                  break;
              case "滿300返100":
                  cashsuper = new CashReturn(300,100);
                  break;
              case "滿200返50":
                  cashsuper = new CashReturn(200, 50);
                  break;
            }
            return cashsuper;
        }
    }
}
//6:2-Strategy.Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AbstractClass;
namespace _2_Strategy
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        /// <summary>
        /// 确定
        /// </summary>
        double douTotal = 0.0d;//總價
        private void btnOK_Click(object sender, EventArgs e)
        {
            double douTotalPrice = 0d;//合計
            //利用簡單工廠模式根據下拉清單框中選中的項生成相應的對象
            CashSuper cashsuper = CashFactory.CashFactory.CreateCashAccpet(this.cboType.SelectedItem.ToString());
            douTotalPrice = cashsuper.AccpetCash(Convert.ToDouble(this.txtPrice.Text) * Convert.ToDouble(this.txtNum.Text));
            douTotal = douTotal + douTotalPrice;
            this.lvwProduct.Items.Add("單價:" + this.txtPrice.Text + " 數量:" + this.txtNum.Text + " 合計:" + douTotalPrice.ToString());
            this.lblResult.Text = douTotal.ToString();
        }
        
        /// <summary>
        /// 重置
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnReset_Click(object sender, EventArgs e)
        {
            this.txtPrice.Text = "";
            this.txtNum.Text = "";
            this.lvwProduct.Items.Clear();
            this.lblResult.Text = "0.00";
        }
    }
}      

運作效果圖

  四:第三次改版後的代碼v1.3

  好了,說到這裡,我們看看下面的政策模式UML類圖了,也看下政策模式的基本代碼結構,我們現在要結合我們目前的這個商城收銀軟體這個活生生的例子和政策模式來分析一下了,我們目前的v1.2版本的架構設計,CashSuper就是抽象政策,而正常收費CashNormal,打折收費CashRebate,返利收費CashReturn是三個具體的政策,也就是說是政策模式中具體的算法,對比之後發現v1.2版的代碼欠缺的就是一個承上啟下的Context這個上下文類,同時多了CashFactory這個收費對象生成工廠類,我們現在根據政策模式的UML類圖來開始修複我們的v1.2版的代碼,在做之前我們還是先解析一下我們的UML類圖

  4.1:政策模式結構圖

  矩形框分為三層,它代表一個類,類圖分為三層,第一層為類名,如果是抽象類,則用斜體表示,第二層為類的特性,通常指的是字段和屬性,第三層是類的操作,通常指的是方法和行為,前面如果為“+”表示public,如果為“-”表示private,如果為“#”表示protected,在這幅UMLl類圖中共出現了2個關系,一個是繼承關系:用空心三角形+實作來表示,還有一種是聚合關系:所謂聚合關系表示一種弱的“擁有”關系,展現的是A對象可以包含B對象,但是B對象不是A對象的一部分,舉個例子,大雁是群居動物,每隻大雁屬于一個雁群,一個雁群可以有多隻大雁,但是每隻大雁又不是雁群的一部分,這裡雁群是A對象,每隻大雁是B對象,再舉個例子,機場包含很多飛機,一個機場可以停放多架飛機,一個機場可以包含多架飛機,但是你不能說每個飛機是機場本身的一部分一樣,聚合關系用空心的菱形+實線箭頭來表示

  

  具體到我們現在的執行個體上

  

  4.2:政策模式基本代碼

   Strategy類,定義所有的支援的算法的公共接口

abstract class Strategy
{
  //算法方法
  public abstract AlgorithmInterface()
}

//ConcretStrategy類,封裝了具體的算法和行為,繼承自Strategy類
//具體算法A
class ConcretStrategyA :Strategy
{
   public AlgorithmInterface()
  {
   Console.WriteKine(“算法A實作");
  }
}

//具體算法B
class ConcretStrategyB :Strategy
{
  public AlgorithmInterface()
  {
   Console.WriteKine(“算法B實作");
  }
}

//具體算法C
class ConcretStrategyC :Strategy
{
  public AlgorithmInterface()
  {
   Console.WriteKine(“算法C實作");
  }
}
 
//上下文Context,用一個ConcretStrategy具體算法來配置,維護一個對Strategy對象的引用
class Context
{
  Strategy _strategy;
  //初始化時,傳入具體的政策對象
  public Context(Strategy strategy)
  {
    this._strategy = strategy;
  }

  //上下文接口,根據傳入的具體政策對象,調用其算法的方法
  public void ContextInterface()
  {
    this._strategy.AlgorithmInterface();
  }
}

  //用戶端代碼
  //由于傳入的具體政策對象,是以在調用context.ContextInterface()方法時,得到的是具體政策對象的算法
  Context context = new Context (new ConcretStrategyA());
  context.ContextInterface();
  Context context = new Context (new ConcretStrategyB());
  context.ContextInterface();
  Context context = new Context (new ConcretStrategyC());
  context.ContextInterface();      

  4.3:New Question?

   在v1.2版的代碼上我們根據政策模式的UML類圖,增加一個CashContext類,并修改用戶端就行了,但是這樣又會帶來一個新的問題?什麼問題呢?因為去掉了CashFactory這個收費對象生成工廠,是以變成了在用戶端去判斷用哪一個具體的算法?我們應該怎樣才能把這個判斷的過程從用戶端移走呢?

  4.4:Answer

  在v1.2版的代碼裡我們是通過簡單工廠模式去轉移走的,但是現在這裡隻有一個CashContext類,簡單工廠并不是一定是一個單獨的類,它為什麼不能和政策模式中的CashContext類結合使用呢?

回答正确

public Class CashContext
{
    //聲明一個CashSuper對象
  private CashSuper _cashSuper;

  /// <summary>
  /// 通過構造函數傳入具體的政策
  /// </summary>
  /// <param name="cashsuper">具體的收費政策</param>
  public CashContext(CashSuper cashsuper)
  {
    this._cashSuper = cashsuper;
  }

  /// <summary>
  /// 根據具體收費政策的不同,獲得不同的計算結果 
  /// </summary>
  /// <param name="douMoney">原價</param>
  /// <returns>目前價</returns>
  public double GetMoney(double douMoney)
  {
    return this._cashSuper.AcceptCash(douMoney);
  }
}

/// <summary>
/// 确定
/// </summary>
double douTotal = 0.0d;//總價
private void btnOK_Click(object sender, EventArgs e)
{
    double douTotalPrice = 0d;//合計
    CashContext cashContext = null;
    switch(this.cbType.SelectedItem.ToString())
    {
        case "正常收費":
            cashContext = new CashContext(new CashNormal());
            break;
        case "打八折":
            cashContext =  new CashContext(new CashRebate(0.8));
            break;
        case "打七折":
            cashContext =  new CashContext(new CashRebate(0.7));
            break;
        case "打五折":
            cashContext =  new CashContext(new CashRebate(0.5));
            break;
        case "打三折":
            cashContext =  new CashContext(new CashRebate(0.3));
            break;
        case "滿300返100":
            cashContext =  new CashContext(new CashReturn(300,100));
            break;
        case "滿200返50":
            cashContext =  new CashContext(new CashReturn(200, 50));
            break;
    }
    //通過CashContext 的GetMoney()方法的調用,可以得到具體收費政策的計算結果,讓具體算法與用戶端進行了分離
    douTotalPrice = cashContext.GetMoney(Convert.ToDouble(this.txtPrice.Text) * Convert.ToDouble(this.txtNum.Text));
    douTotal = douTotal + douTotalPrice;
    this.lvwProduct.Items.Add("單價:" + this.txtPrice.Text + " 數量:" + this.txtNum.Text + " 合計:" + douTotalPrice.ToString());
    this.lblResult.Text = douTotal.ToString();
}      

  六:第四次改版後的代碼v1.4

  6.1:為什麼要改版?

  在用戶端去判斷用哪一個具體算法,我們應該把這個判斷的過程從用戶端移走

  政策模式和簡單工廠模式結合使用

  修改CashContext類

public Class CashContext
{
    //聲明一個CashSuper對象
    CashSuper _cashSuper;
    /// <summary>
    /// 構造函數參數不在是一個具體政策對象,而是一個字元串表示收費類型
    /// </summary>
    /// <param name="strTyle">收費類型</param>
    public CashContext(string strTyle)
    {
        case "正常收費":
            _cashSuper = new CashNormal();
            break;
        case "打八折":
            _cashSuper =  new CashRebate(0.8);
            break;
        case "打七折":
            _cashSuper =  new CashRebate(0.7);
            break;
        case "打五折":
            _cashSuper =  new CashRebate(0.5);
            break;
        case "打三折":
            _cashSuper =  new CashRebate(0.3);
            break;
        case "滿300返100":
            _cashSuper =  new CashReturn(300,100);
            break;
        case "滿200返50":
            _cashSuper =  new CashReturn(200, 50);
            break;
}

    /// <summary>
    /// 根據具體收費政策的不同,獲得不同的計算結果 
    /// </summary>
    /// <param name="douMoney">原價</param>
    /// <returns>目前價</returns>
    public double GetMoney(double douMoney)
    {
        this._cashsuper.AcceptCash(douMoney);
    }
}

/// <summary>
/// 确定修改用戶端代碼 Strategy.Form1.cs
/// </summary>
double douTotal = 0.0d;//總價
private void btnOK_Click(object sender, EventArgs e)
{
    double douTotalPrice = 0d;//合計
    //根據下拉清單框,将相應的字元串傳入到CashContext對象中
    CashContext cashContext= new CashContext(this.cbType.SelectedItem.ToString());
    //通過CashContext的GetMoney()方法的調用,可以得到具體收費政策的計算結果,讓具體算法與用戶端進行了分離
    douTotalPrice = cashContext.GetMoney(Convert.ToDouble(this.txtPrice.Text) * Convert.ToDouble(this.txtNum.Text));
    douTotal = douTotal + douTotalPrice;
    this.lvwProduct.Items.Add("單價:" + this.txtPrice.Text + " 數量:" + this.txtNum.Text + " 合計:" + douTotalPrice.ToString());
    this.lblResult.Text = douTotal.ToString();
}      

  七:對比v1.2版的簡單工廠模式和v1.4版的政策模式在用戶端的代碼對比

  我們發現簡單工廠模式,用戶端我們需要認識到兩個類,一個是:”CashSuper“類,另外一個則是:”CashFactory“類,而政策模式和簡單工廠模式的結合,使得用戶端隻是需要認識一個CashContext 類就可以了,耦合度更加降低了,我們在用戶端執行個體化的是CashContext對象,調用的是CashContext對象的GetMoney()方法,這使得具體的收費政策和用戶端分離開來,連父類”CashSuper“類都不讓用戶端看見

    利用簡單工廠模式根據下拉清單框中選中的項生成相應的對象

CashSuper cashsuper = CashFactory.CashFactory.CreateCashAccpet(this.cboType.SelectedItem.ToString());
douTotalPrice = cashsuper.AccpetCash(Convert.ToDouble(this.txtPrice.Text) * Convert.ToDouble(this.txtNum.Text));
douTotal = douTotal + douTotalPrice;      

  政策模式 

//根據下拉清單框,将相應的字元串傳入到CashContext對象中
CashContext cashcontext= new CashContext(this.cbType.SelectedItem.ToString());
//通過CashContext 的GetMoney()方法的調用,可以得到具體收費政策的計算結果,讓具體算法與用戶端進行了分離
douTotalPrice = cashcontext .GetMoney(Convert.ToDouble(this.txtPrice.Text) * Convert.ToDouble(this.txtNum.Text));      

  八:政策模式思考

  政策模式是一種定義了一系列算法的方法,所有這些算法完成的都是相同的工作(比如:得到對商品要收取的實際價格AcceptCash(double douMoney)),隻是實作不同罷了,它可以以相同的方式調用所有的算法(比如:GetMoney()這個相同的方式),減少了各個算法類和使用算法類用戶端之間的耦合性(修改各個具體算法類之間不影響,而且用戶端看不到具體的算法類)

  政策模式中為Context定義了一系列的可供重用的算法或者行為,繼承有助于析取這些算法的公共功能,無論對于打折,返利,返積分,其實都是對商品要收取的實際價格的一種計算方式,通過繼承,就可以得到它們的公共功能,公共功能就是指的是Context類中定義的GetMoney()這個方法,這便使得算法間有了抽象的父類CashSuper

  修改其中任何一個算法類,不會影響到其他算法類,可以保證每個算法中不出現錯誤

  政策模式是用來封裝算法的,但是在應用中,可以用來封裝任何類型的規則

  在基本的政策模式中,選擇具體的政策由用戶端承擔,并轉給政策模式中的Contenxt對象,如v1.3版本中的代碼,這沒有解決用戶端需要選擇判斷分支的壓力,而政策模式和簡單工廠模式的結合,選擇具體的政策由用戶端承擔轉移到了由Context來承擔,這就大大的減輕了用戶端的職責,如v1.4版代碼,政策模式和簡單工廠模式的結合雖然把選擇具體的政策由用戶端轉移到了Context,但是在Context中還是存在switch判斷分支語句啊,也就是說我們增加一種算法,比如”打8.8折“,就必須得到switch裡面去增加分支判斷,商場打折折扣率一變,我們還得去更改分支,這就是我前面提到的一個問題,由于Contenxt本身包含了所有的收費方式,我想每一個商場一定會經常變動打折折扣率和返利額度的,每次維護和擴充收費方式都要改動這個Contenxt類,以至于代碼要重新編譯和部署,怎麼才能連這裡面的分支判斷都去掉呢?大家想想看,有什麼辦法?沒錯這個辦法就是反射