在講策略模式之前,我們先看一個日常生活中的小例子: 現實生活中我們到商場買東西的時候,賣場往往根據不同的客戶制定不同的報價策略,比如針對新客戶不打折扣,針對老客戶打9折,針對VIP客戶打8折... 現在我們要做一個報價管理的模塊,簡要點就是要針對不同的客戶,提供不同的折扣報價。如果是有你來做...
在講策略模式之前,我們先看一個日常生活中的小例子:
現實生活中我們到商場買東西的時候,賣場往往根據不同的客戶制定不同的報價策略,比如針對新客戶不打折扣,針對老客戶打9折,針對VIP客戶打8折...
現在我們要做一個報價管理的模塊,簡要點就是要針對不同的客戶,提供不同的折扣報價。
如果是有你來做,你會怎麼做?
我們很有可能寫出下麵的代碼:
package strategy.examp02;
import java.math.BigDecimal;
public class QuoteManager {
public BigDecimal quote(BigDecimal originalPrice,String customType){
if ("新客戶".equals(customType)) {
System.out.println("抱歉!新客戶沒有折扣!");
return originalPrice;
}else if ("老客戶".equals(customType)) {
System.out.println("恭喜你!老客戶打9折!");
originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);
return originalPrice;
}else if("VIP客戶".equals(customType)){
System.out.println("恭喜你!VIP客戶打8折!");
originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);
return originalPrice;
}
//其他人員都是原價
return originalPrice;
}
}
經過測試,上面的代碼工作的很好,可是上面的代碼是有問題的。上面存在的問題:把不同客戶的報價的演算法都放在了同一個方法裡面,使得該方法很是龐大(現在是只是一個演示,所以看起來還不是很臃腫)。
下麵看一下上面的改進,我們把不同客戶的報價的演算法都單獨作為一個方法
1 package strategy.examp02;
2
3 import java.math.BigDecimal;
4
5 public class QuoteManagerImprove {
6
7 public BigDecimal quote(BigDecimal originalPrice, String customType){
8 if ("新客戶".equals(customType)) {
9 return this.quoteNewCustomer(originalPrice);
10 }else if ("老客戶".equals(customType)) {
11 return this.quoteOldCustomer(originalPrice);
12 }else if("VIP客戶".equals(customType)){
13 return this.quoteVIPCustomer(originalPrice);
14 }
15 //其他人員都是原價
16 return originalPrice;
17 }
18
19 /**
20 * 對VIP客戶的報價演算法
21 * @param originalPrice 原價
22 * @return 折後價
23 */
24 private BigDecimal quoteVIPCustomer(BigDecimal originalPrice) {
25 System.out.println("恭喜!VIP客戶打8折");
26 originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);
27 return originalPrice;
28 }
29
30 /**
31 * 對老客戶的報價演算法
32 * @param originalPrice 原價
33 * @return 折後價
34 */
35 private BigDecimal quoteOldCustomer(BigDecimal originalPrice) {
36 System.out.println("恭喜!老客戶打9折");
37 originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);
38 return originalPrice;
39 }
40
41 /**
42 * 對新客戶的報價演算法
43 * @param originalPrice 原價
44 * @return 折後價
45 */
46 private BigDecimal quoteNewCustomer(BigDecimal originalPrice) {
47 System.out.println("抱歉!新客戶沒有折扣!");
48 return originalPrice;
49 }
50
51 }
上面的代碼比剛開始的時候要好一點,它把每個具體的演算法都單獨抽出來作為一個方法,當某一個具體的演算法有了變動的時候,只需要修改響應的報價演算法就可以了。
但是改進後的代碼還是有問題的,那有什麼問題呢?
1.當我們新增一個客戶類型的時候,首先要添加一個該種客戶類型的報價演算法方法,然後再quote方法中再加一個else if的分支,是不是感覺很是麻煩呢?而且這也違反了設計原則之一的開閉原則(open-closed-principle).
開閉原則:
對於擴展是開放的(Open for extension)。這意味著模塊的行為是可以擴展的。當應用的需求改變時,我們可以對模塊進行擴展,使其具有滿足那些改變的新行為。也就是說,我們可以改變模塊的功能。
對於修改是關閉的(Closed for modification)。對模塊行為進行擴展時,不必改動模塊的源代碼或者二進位代碼。
2.我們經常會面臨這樣的情況,不同的時期使用不同的報價規則,比如在各個節假日舉行的各種促銷活動時、商場店慶時往往都有普遍的折扣,但是促銷時間一旦過去,報價就要回到正常價格上來。按照上面的代碼我們就得修改if else裡面的代 碼很是麻煩
那有沒有什麼辦法使得我們的報價管理即可擴展、可維護,又可以方便的響應變化呢?當然有解決方案啦,就是我們下麵要講的策略模式。
定義:
策略模式定義了一系列的演算法,並將每一個演算法封裝起來,使每個演算法可以相互替代,使演算法本身和使用演算法的客戶端分割開來,相互獨立。
結構:
1.策略介面角色IStrategy:用來約束一系列具體的策略演算法,策略上下文角色ConcreteStrategy使用此策略介面來調用具體的策略所實現的演算法。
2.具體策略實現角色ConcreteStrategy:具體的策略實現,即具體的演算法實現。
3.策略上下文角色StrategyContext:策略上下文,負責和具體的策略實現交互,通常策略上下文對象會持有一個真正的策略實現對象,策略上下文還可以讓具體的策略實現從其中獲取相關數據,回調策略上下文對象的方法。
UML類圖: