設計模式——策略模式
1. 簡單工廠實現
面向對象的編程,並不是類越多越好,類的劃分是為了封裝,但分類的基礎是抽象,具有相同屬性和功能的對象的抽象集合才是類。
2. 策略模式
策略這個詞應該怎麼理解,打個比方說,我們出門的時候會選擇不同的出行方式,比如騎自行車、坐公交、坐火車、坐飛機、坐火箭等等,這些出行方式,每一種都是一個策略。
再比如我們去逛商場,商場現在正在搞活動,有打折的、有滿減的、有返利的等等,其實不管商場如何進行促銷,說到底都是一些算法,這些算法本身只是一種策略,並且這些算法是隨時都可能互相替換的,比如針對同一件商品,今天打八折、明天滿100減30,這些策略間是可以互換的。
策略模式(Strategy):它定義了算法家族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化,不會影響到使用算法的客戶。
策略模式是一種定義一系列算法的方法,從概念上來看,所有這些算法完成的都是相同的工作,只是實現不同,它可以以相同的方式調用所有的算法,減少了各種算法類與使用算法類之間的耦合。
策略模式的策略類(Strategy)類層次為上下文(Context)定義了一系列的可供重用的算法或行為。繼承有助於析取出這些算法中的公共功能。
策略模式的優點是簡化了單元測試,因為每個算法都有自己的類,可以通過自己的接口單獨測試。
當不同的行為堆砌在一個類中時,就很難避免使用條件語句來選擇合適的行為。將這些行為封裝在一個獨立的Strategy類中,可以在使用這些行為的類中消除條件語句。
策略模式就是用來封裝算法的,但在實踐中,我們發現可以用它來封裝幾乎任何類型的規則,只要在分析過程中聽到需要在不同時間應用不同的業務規則,就可以考慮使用策略模式處理這種變化的可能性。
策略模式和簡單工廠模式的區別:
簡單工廠模式:只需要發出命令,由他人去實現。
策略模式:不知要發出命令,還得由自己親自去做。
3. 策略模式的應用
1). 何時使用:一個系統有許多類,而區分它們的只是他們直接的行為時
2). 方法:將這些算法封裝成一個一個的類,任意的替換
3). 優點:算法可以自由切換;避免使用多重條件判斷(如果不用策略模式我們可能會使用多重條件語句,不利於維護);擴展性良好,增加一個策略只需實現接口即可
4). 缺點:策略類數量會增多,每個策略都是一個類,復用的可能性很小;所有的策略類都需要對外暴露
5). 使用場景:多個類只有算法或行為上稍有不同的場景;算法需要自由切換的場景;需要屏蔽算法規則的場景
6). 應用實例:出行方式,自行車、汽車等,每一種出行方式都是一個策略;商場促銷方式,打折、滿減等;Java AWT中的LayoutManager,即布局管理器
7). 注意事項:如果一個系統的策略多於四個,就需要考慮使用混合模式來解決策略類膨脹的問題
4. 應用場景實例
(1)假設現在要設計一個販賣各類書籍的電子商務網站的購物車系統。一個最簡單的情況就是把所有貨品的單價乘上數量,但是實際情況肯定比這要複雜。比如,本網站可能對所有的高級會員提供每本20%的促銷折扣;對中級會員提供每本10%的促銷折扣;對初級會員沒有折扣。
根據描述,折扣是根據以下的幾個算法中的一個進行的:
算法一:對初級會員沒有折扣。
算法二:對中級會員提供10%的促銷折扣。
算法三:對高級會員提供20%的促銷折扣。
抽象折扣類:
初級會員折扣類:
中級會員折扣類:
高級會員折扣類:
價格類:
調用:
(2)商城收銀軟體,營業員根據客戶所購買商品的單價和數量,向客戶收費。現在商場對商品搞活動,所有的商品對應不同的情況打八折、七折、五折,並且滿300返100、滿200返50。商場增加促銷手段,滿100積分10點,以後積分累積到一定數量可以兌換獎品。
CashContext類:
Class CashContext{
//聲明一個CashSuper對象
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折";
CashRebate cr2 = new CashRebate("0.8");
cs = cr2;
break;
}
}
public double GetResult(double money){
return cs.acceptCash(money);
}
}
客戶端代碼(主要部分):
double total = 0.0d;
private void btnOk_Click(object sender, EventArgs e){
//將相應的算法類型字符串傳入CashContext的對象中
CashContext csuper = new CashContext(cbxType.SelectedItem.ToString());
……
}