#策略模式
##應用場景
###一個商場收銀軟件,營業員根據客戶所購買的商品的單價和數量,向客戶收費
用兩個文本框來輸入單價和數量,一個確定按鈕來算出每種商品的費用,用個列表框來記錄商品的清單,一個標簽來記錄總計,一個重置按鈕來重新開始。
double total = 0.0d;
private void btn0k_Click(object sender, EventArgs e)
{
double totalPrices = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text);
total = total + totalPrices;
IbxList.Items.Add("單價:" +txtPrice.Text + "數量:" + txtNum.Text + "合計:"+ totalPrices.ToString());
IblResult.Text = total.ToString();
}
###比如遇到節假日 增加打折
double total = 0.0d;
private void Form_Load(object sender, EventArgs e)
{
cbxType.Items.AddRange(new object[] {"正常收費","打八折","打五折"});
cbxType.SlectedIndex = 0;
}
private void btn0k_Click(object sender, EventArgs e)
{
double totalPrices = 0.0d;
switch(cbxType.Selectedindex)
{
case 0:
totalPrices = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text);
case 1:
totalPrices = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text)*0.8;
case 2:
totalPrices = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text)*0.5;
}
total = total + totalPrices;
IbxList.Items.Add("單價:" +txtPrice.Text + "數量:" + txtNum.Text + "合計:"+ totalPrices.ToString());
IblResult.Text = total.ToString();
}
##簡單工廠實現
面對對象的編程,并不是類越多越好,類的劃分是為了封裝,但是分裝的基礎是抽象,具有相同屬性和功能的對象的抽象集合才是類。打一折和打九折只是形式的不同,抽象分析出來,所有的打折算法都是一樣的,所以打折算法應該是一個類。
//現金收費抽象類
abstract class CashSuper
{
public abstract double acceptCash(double money);
}
//正常收費子類
class CashNormal: CashSuper
{
public override double acceptCash(double money)
{
return money;
}
}
//打折收費子類
class CashRebate: CashSuper
{
private double moneyRebate = 1d;
public Cash Rebebate(string moneyRebate)
{
this.moneyRebate = double.Parse(moneyRebate);
}
publci override double acceptCash(double money)
{
return money * moneyRebate;
}
}
//返利收費子類
class cashReturn: CashSuper
{
private double moneyCondition = 0.0d;
private double moneyreturn = 0.0d;
public CashReturn(string moneyCondition, string moneyReturn)
{
this.moneyCondition = double.Parse(moneyCondtion);
this.moneyReturn = double.Parse(moneyReturn);
}
public override double acceptCash(double money)
{
double result = money;
if(money >= moneyCondition)
{
result = money - Math.Floor(money / moneyCondition) * moneyReturn;
}
return result;
}
}
//現金收費工廠類
class CashFactory
{
public static CashSuper createCashAccept(string type)
{
CashSuper cs = null;
switch (type)
{
case:"正常收費":
cs = new CashNormal();
break;
case:"滿300返100":
CashReturn cr1 = new CashReturn("300","100");
cs = cr1;
break;
case:"打8折":
CashRebate cr2 = new CashRebate("0,8");
cs = cr2;
break;
}
return cs;
}
}
//客戶端程序主要部分
double total = 0.0d;
private void btn0k_clik(object sneder, EventArgs e)
{
cashSuper csuper = CashFactory.createCashAccept(cbxType.SelectedItem.ToString());
double totalPrices = 0d;
totalPrices = csuper.acceptCash(Convert.ToDouble(txtPrice.txt)*Convert.ToDouble(txtNum.Text));
total = total + totalPrices;
IbxList.Items.Add("單價:" +txtPrice.Text + "數量:" + txtNum.Text + "合計:"+ totalPrices.ToString());
IblResult.Text = total.ToString();
}
簡單工廠模式雖然能解決這個問題,但是這個模式知識解決對象的創建問題,而且由于工廠本身包括了所有的收費方式,商場是可能經常性地更改打折額度和返利額度,每次維護或擴展收費方式都要改動這個工廠,以致代碼需重新編譯部署,這真的是很糟糕的處理方式,所以用它不是做好的辦法,。面對算法的時常變動應該有更好的辦法。
##策略模式
策略模式定義了算法家族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化,不會影響到使用算法的客戶。
//CashContext類
class CashContext
{
private CashSuper cs;
public CashContext(CashSuper csuper)
{
this.cs = csuper;
}
public double GetResult(double money)
{
return cs.accptCash(money);
}
}
//客戶端主要代碼
double total = 0.0d;
private void btn0k_Click(object sender, EventArgs e)
{
CashContext cc = null;
switch (cbxType.selectedItem.ToString())
{
case: "正常收費":
cc = new CashContext(new CashNormal());
break;
case: "滿300返100":
cc = new CashContext(new CashReturn ("300","100"));
break;
case: "打8折":
cc = new CashContext(new CashRebate("0.8"));
break;
}
double totalPrices = 0d;
totalPrices = cc.GetResult(convert.ToDouble(txtPrice.Text)*convert.ToDouble(txtNum.Text));
total = total + totalPrices;
IbxList.Items.Add("單價:" +txtPrice.Text + "數量:" + txtNum.Text + "合計:"+ totalPrices.ToString());
IblResult.Text = total.ToString();
}
雖然策略模式寫出來了,但是不應該讓客戶端去判斷用哪一個算法。
##策略與簡單工廠結合
class CashContext
{
CashSuper cs = null;
public CashContext(String type)
{
switch(type)
{
case "正常收費":
CashNormal cs0 = new CashNormal();
cs = cs0;
break;
case "滿300返100":
CashReturn cr1 = new CashReturn("300","100");
cs = cr1;
break;
case "打8折":
CashReturn cr2 = new CashRebate("0.8");
cs = cr2;
break;
}
public double GetResult(double money)
{
return cs.acceptCash(money);
}
}
}
//客戶端代碼
double total = 0.0d;
private void btn0k_Click(object sender, EvnetArgs e)
{
CashContext csuper = new CashContext(cbxType.slectedItem.ToString());
double totalPrices = 0d;
totalPrices = csuper.GetResult(Convert.ToDouble(txtPrice.Text)*Convert.ToDouble(txtNum.Text));
total = total + totalPrices;
IbxList.Items.Add("單價:" +txtPrice.Text + "數量:" + txtNum.Text + "合計:"+ totalPrices.ToString());
IblResult.Text = total.ToString();
}
//簡單工廠模式的用法
CashSuper csuper = CashFactory.CreateCashAccept(cbxType.SelectedItem.ToString());
=csuper.GetResult;
//策略模式與簡單工廠結合的用法
CashContext csuper = new (CashContext(cbxType.SelectedItem.ToString()));
=csuper.GetResult;
簡單工廠模式讓客戶端認識兩個類, CashSuper和CashFactory,而策略模式與簡單工廠結合的用法,客戶端只需要認識一個類CashContext就可以了。耦合更加降低。
##策略模式解析
策略模式是一種定義一系列算法的方法,從概念上來看,所有這些算法完成的都是相通的工作,只是實現不同,它可以以相同的方式調用所有的算法,減少各種算法類和使用算法類之間的耦合。
策略模式就是用來封裝算法的,但是實踐中,我們發現可以用它來分裝幾乎任何類型的規則,只要在分析過程中聽到需要在不同的時間應用不同的業務規則,就可以考慮使用策略模式處理這種變化的可能性。