開篇
今天我們來討論一下設計模式中最常用的一種行爲模式----策略模式,在實際的項目中,我們經常會解決類似這樣的業務場景,比如,對於同一件事情,在不同時間或不同狀態下,執行不同的邏輯,我們就可以通過策略模式來封裝具體的算法規則,實際使用模塊只需要知道使用何種策略即可,今天我們就通過兩個數的運算來模擬多規則下策略模式如何設計並改善。
基本介紹
策略模式(Strategy Pattern)中,定義出一套算法族,讓他們可以根據不同場景進行替換,同時通過定義配置對象來對外提供相應的算法對象。目的是解決多場景下,核心處理邏輯類似,判斷語句過多的問題。
主要解決
- 通過if----else判斷將不同類型的業務邏輯聚合在一起時,維護成本增高,耦合性高,可讀性低。
- 所有邏輯糅合在一起,當有一處發生變化時,帶動全體一起修改。
- 不能進行單元測試,或單元測試不便捷。
關鍵代碼
- 封裝算法接口。
- 多種算法類實現總接口。
- 提供配置對象給客戶端調用算法類。
應用場景
- 本文案例,根據不同類型,對兩個數執行不同運算邏輯。
- 不同時間段,商場開展不同促銷活動。
- 旅行出遊,選擇不同交通工具,前往目的地。
優點
- 實際的算法和使用算法類直接耦合性減少。
- 減少大量的判斷邏輯,將不同邏輯拆開,便於維護。
- 簡化單元測試,可以針對性對某一策略進行測試和維護。
缺點
- 場景增多時,策略類會增多,造成類膨脹。
- 還是需要通過判斷來尋找到具體策略。
- 算法實現類超過4個時,需要使用組合模式。
案例
給定兩個數,num1,num2,分別計算加減乘除。
1、定義邏輯處理總接口
/**
* 數據計算接口
* 優點
* 定義數據計算的統一接口,
* 便於後續使用時不關心每個類中具體方法的定義,
* 實現類可以實現多個接口,因此此處不用抽象類。
* 缺點
* 1、該接口存在多個方法,而後續操作有些不適用,就需要實現很多無用方法,增加代碼量。
* 2、當接口類中增加一個新的方法時,實現類必須全體一起增加該方法的實現。
*/
public interface CalculationInterface {
//用於處理兩個數的運算
public int mathod(int num1, int num2);
}
2、實現加減乘除算法類
/**
* 數據處理實現:加法策略實現類
*/
public class Addition implements CalculationInterface {
/**
* @param num1 數據1
* @param num2 數據2
* @return 兩數之和
*/
@Override
public int mathod(int num1, int num2) {
return num1 + num2;
}
}
/**
* 數據處理實現:減法策略實現類
*/
public class Subtraction implements CalculationInterface {
/**
* @param num1 數據1
* @param num2 數據2
* @return 兩數相減
*/
@Override
public int mathod(int num1,int num2) {
return num1 - num2;
}
}
/**
* 數據處理實現:乘法策略實現類
*/
public class Multiplication implements CalculationInterface {
/**
* @param num1 數據1
* @param num2 數據2
* @return 兩數相乘
*/
@Override
public int mathod(int num1, int num2) {
return num1 * num2;
}
}
/**
* 數據處理實現:除法策略實現類
*/
public class Division implements CalculationInterface {
/**
* @param num1 數據1
* @param num2 數據2 不可以爲0
* @return 兩數相除
*/
@Override
public int mathod(int num1, int num2) {
if (num2 == 0) throw new RuntimeException("除數不能爲0");
return num1 / num2;
}
}
3、構建配置對象
/**
* 提供配置環境
* 統一策略的創建,
* 統一策略方法的使用,
* 對外不需要關心具體策略內部如何實現,
* 不需要關心如何創建相關策略類,執行相應策略的方式,
* 只需要知道有哪些策略,該使用哪一策略。
*/
public class Context {
private CalculationInterface anInterface;
//通過統一配置環境,創建不同策略實現類。
public Context(CalculationInterface anInterface) {
this.anInterface = anInterface;
}
//提供參數統一入口,根據創建不同的實現類,執行不同策略,
public int logic(int num1, int num2) {
return anInterface.mathod(num1, num2);
}
}
4、策略模式執行類
/**
* 策略模式測試
*/
public class StrategyTest {
public static void main(String[] args) {
int add = 0, sub = 0, mul = 0, div = 0;
if (StrategyType.getAdd().equals("加")) {
Context addition = new Context(new Addition());
add = addition.logic(400, 200);
}
if (StrategyType.getSub().equals("減")) {
Context subtraction = new Context(new Subtraction());
sub = subtraction.logic(400, 200);
}
if (StrategyType.getMul().equals("乘")) {
Context multiplication = new Context(new Multiplication());
mul = multiplication.logic(400, 200);
}
if (StrategyType.getDiv().equals("除")) {
Context division = new Context(new Division());
div = division.logic(400, 200);
}
System.out.println("加:" + add + " 減:" + sub + " 乘:" + mul + " 除:" + div);
}
}
5、定義策略類型
/**
* 定義策略類型
*/
public class StrategyType {
private static final String add = "加";
private static final String sub = "減";
private static final String mul = "乘";
private static final String div = "除";
public static String getAdd() {
return add;
}
public static String getSub() {
return sub;
}
public static String getMul() {
return mul;
}
public static String getDiv() {
return div;
}
}
以上就是策略模式的具體使用方式,但是我們不難發現,還是需要客戶端去判斷不同類型下,如何創建,不僅比較繁瑣,同時還需要知道具體策略才行,這對客戶端來說要求太高了。
改進:策略+簡單工廠模式
我們將客戶端判斷部分封裝到配置類中,這樣客戶端不需要關心何種類型下如何實現對應算法,只需要實現配置類即可,簡化操作。
1、改進配置類
/**
* 配置類改善
* 原因
* 使用策略模式後,客戶端依然需要判斷在具體條件下,創建具體的策略,因此改造配置類
* 將判斷創建具體策略的行爲從客戶端隔離,讓客戶端徹底不再判斷
* 方案
* 創建具體策略時傳入相關類型,交給配置類進行判斷
*/
public class ContextImprove {
private CalculationInterface anInterface;
//通過統一配置環境,創建不同策略實現類。
public ContextImprove(String strategyType) {
if (strategyType.equals("加")) {
this.anInterface = new Addition();
}
if (strategyType.equals("減")) {
this.anInterface = new Subtraction();
}
if (strategyType.equals("乘")) {
this.anInterface = new Multiplication();
}
if (strategyType.equals("除")) {
this.anInterface = new Division();
}
}
//提供參數統一入口,根據創建不同的實現類,執行不同策略,
public int logic(int num1, int num2) {
return anInterface.mathod(num1, num2);
}
}
2、測試
/**
* 策略模式測試
*/
public class StrategyTest {
public static void main(String[] args) {
ContextImprove contextImprove = new ContextImprove(StrategyType.getAdd());
System.out.println(StrategyType.getAdd() + ":" + contextImprove.logic(100, 200));
}
}
此時我們可以看到,客戶端使用時根本不需要關心如何創建相應的算法類,只需要創建配置類即可,但是不難發現上面依然存在很多判斷在配置類中,這裏可以用抽象工廠來替換掉,本文不做敘述,大家可以自行研究。
總結
- 在處理業務需求時,如果針對同一事情,在不同場景下進行不同操作,我們可以使用策略模式減少耦合性。
- 策略模式的使用可以讓單元測試更加的便捷。
- 實際業務中,我們往往通過組合模式來解決問題,掌握核心設計模式的同時,還要靈活輔助其他模式。