定義:
策略模式定義了一系列算法,並將每一個算法封裝起來,而且使他們可以相互替換,讓算法獨立於使用它的客戶而獨立變化。
結構:
策略接口角色(Strategy):定義所有支持的算法的公共接口
具體策略實現角色(ConcreteStrategy):具體的策略實現
策略上下文角色(StrategyContext):策略上下文負責和具體的策略實現交互,通常策略上下文對象會持有一個真正的策略實現對象,策略上下文還可以讓具體的策略實現從其中獲取相關數據,回調策略上下文對象的方法。
實際場景:
假如,現在有這樣一個需求,做一個簡單的商場收銀功能,普通用戶不打折,新用戶打五折,VIP用戶打六折,給出一個Java的 demo案例:
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;
}
}
上面的代碼非常好,簡單直接,但有個問題:所有的算法法都在一個方法裏,假如算法複雜點,就會很龐大,(當然現在還看不出來)
改進一:
剛纔說算法都寫在quote方法裏,非常大,那就提取出來,每個算法定義一個方法,然後去調用這個方法。eg:
public BigDecimal quote(BigDecimal originalPrice, String customType){
if ("新客戶".equals(customType)) {
return this.quoteNewCustomer(originalPrice);
}else if ("老客戶".equals(customType)) {
return this.quoteOldCustomer(originalPrice);
}else if("VIP客戶".equals(customType)){
return this.quoteVIPCustomer(originalPrice);
}
//其他人員都是原價
return originalPrice;
}
/**
* 對VIP客戶的報價算法
* @param originalPrice 原價
* @return 折後價
*/
private BigDecimal quoteVIPCustomer(BigDecimal originalPrice) {
System.out.println("恭喜!VIP客戶打8折");
originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);
return originalPrice;
}
/**
* 對老客戶的報價算法
* @param originalPrice 原價
* @return 折後價
*/
private BigDecimal quoteOldCustomer(BigDecimal originalPrice) {
System.out.println("恭喜!老客戶打9折");
originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);
return originalPrice;
}
/**
* 對新客戶的報價算法
* @param originalPrice 原價
* @return 折後價
*/
private BigDecimal quoteNewCustomer(BigDecimal originalPrice) {
System.out.println("抱歉!新客戶沒有折扣!");
return originalPrice;
}
}
這種方式,在我們平常寫代碼 的時候經常會用到,整體代碼來看,也相對較整潔。但對於這樣一個功能來講,可擴展性不高。假如現在需求要求我們新增加一種客戶優惠類型,首先,需要加一個算法,然後,再去quote方法里加一個判斷的分支(if else )這樣就很麻煩,也違反設計模式的開閉原則。
開閉原則:
對於拓展是開放的:即模塊的行爲可擴展,當需求改變,可以對模塊進行擴展
對於修改是封閉的:即對模塊擴展時,不必改動模塊的源代碼
策略模式實現:(通用模板)
策略接口:
//策略接口
4 public interface IStrategy {
5 //定義的抽象算法方法 來約束具體的算法實現方法
6 public void algorithmMethod();
7 }
具體策略實現:有幾個策略,就寫幾個實現策略接口的實現類
// 具體的策略實現
4 public class ConcreteStrategy implements IStrategy {
5 //具體的算法實現
6 @Override
7 public void algorithmMethod() {
8 System.out.println("this is ConcreteStrategy method...");
9 }
10 }
// 具體的策略實現2
4 public class ConcreteStrategy2 implements IStrategy {
5 //具體的算法實現
6 @Override
7 public void algorithmMethod() {
8 System.out.println("this is ConcreteStrategy2 method...");
9 }
10 }
策略上下文:
/**
4 * 策略上下文
5 */
6 public class StrategyContext {
7 //持有一個策略實現的引用
8 private IStrategy strategy;
9 //使用構造器注入具體的策略類
10 public StrategyContext(IStrategy strategy) {
11 this.strategy = strategy;
12 }
13
14 public void contextMethod(){
15 //調用策略實現的方法
16 strategy.algorithmMethod();
17 }
18 }
外部調用:
public static void main(String[] args) {
//1.創建具體測策略實現
IStrategy strategy = new ConcreteStrategy2();
//2.在創建策略上下文的同時,將具體的策略實現對象注入到策略上下文當中
StrategyContext ctx = new StrategyContext(strategy);
//3.調用上下文對象的方法來完成對具體策略實現的回調
ctx.contextMethod();
}
此時假如增加一個客戶類型,那就是新加一個策略的實現類即可,然後外部直接調用。
策略模式的作用:把具體的算法從實際的業務邏輯中分離出來,成爲獨立類,使得相互之間能夠相互替換。
策略模式在JDK中的實現:
多線程中的線程池,對於多線程,是用工廠模式來創建一個線程池Executor,而Executor的一個實現類ThreadPoolExecutor ,這個類中的 RejectedExecutionHandler 其實就是一個策略接口,用在當線程池中沒有多餘的線程來執行任務,並且保存任務的多列也滿了(指的是有界隊列),對仍在提交給線程池的任務的處理策略。
策略模式的本質是:分離算法,選擇實現。