1 介紹
1.1 什么是策略模式
策略模式簡單的說就是,準(zhǔn)備一組算法,并將每一個算法封裝起來,使得它們可以互換。
策略模式是一種對象行為型模式。
1.2 解決什么樣的問題
策略模式為了解決什么樣的問題呢?我們來看一個例子。
超市隔三差五就要辦促銷活動,每次促銷活動的方式不一樣,希望找到一種方式,使得營業(yè)員只要輸入原價再選擇活動類型后,就能計算出折扣以后的價錢。
面對這個問題,通常我們想到的解決方案是用if...else或者switch語句寫出邏輯分支。這樣做會有兩個問題:
- 如果要增加新的折扣方式,就打破的"開-閉"原則。
2.業(yè)務(wù)邏輯不能復(fù)用。
時間久了這段代碼會漸漸成為面條代碼,環(huán)境和行為會緊密耦合在一起,逐漸腐敗,難以維護(hù)。
2 原理
2.1 UML圖
-
類圖
image.png
類圖清晰的表明了策略模式結(jié)構(gòu)的參與者。
- Strategy:定義所有支持的算法的公共接口。Context使用這個接口來調(diào)用某ConcreteStrategy定義的算法。
- ConcreteStrategy:具體策略。以Strategy接口實現(xiàn)某具體算法。
- Context:上下文,用一個ConcreteStrategy對象來配置;維護(hù)一個對Strategy對象的引用;可定義一個接口來讓Strategy訪問它的數(shù)據(jù)。
-
時序圖
image.png
2.2 代碼示例
Context示例代碼
public class Context{
private Strategy strategy;
Context(Strategy strategy){
this.strategy = strategy;
}
public void contextInterface(){
strategy.strategyInterface();
}
}
Strategy示例代碼
public interface Strategy { //這里用接口描述, 也可以使用抽象類
void strategyInterface();
}
ConcreteStrategy示例代碼
public class ConcreteStrategyA implements Strategy {
@Override
public void strategyInterface() {
System.out.println("this is ConcreteStrategyA ");
}
}
調(diào)用示例
public static void main(String[] args) {
Strategy strategyA = new ConcreteStrategyA();
Context context = new Context(strategyA);
context.contextInterface();
}
運行結(jié)果
this is ConcreteStrategyA
2.3 優(yōu)缺點
-
優(yōu)點
- 非常易于擴(kuò)展
策略模式提供了對“開閉原則”的完美支持,用戶可以在不修改原有系統(tǒng)的基礎(chǔ)上選擇算法(策略),并且可以靈活地增加新的算法(策略)。 - 避免使用多重條件轉(zhuǎn)移語句。
- 算法可以自由切換。
- 非常易于擴(kuò)展
-
缺點
- 客戶端必須知道所有得策略類,并自行決定使用哪一個策略類。這使得策略類定義必須暴露給客戶端。
- 造成很多策略類,帶來額外的維護(hù)開銷,如果策略類超過4個,就需要考慮使用混合模式,解決策略類膨脹的問題。
4 適用場景
在以下情況下可以使用策略模式
- 如果在一個系統(tǒng)里面有許多類,它們之間的區(qū)別僅在于它們的行為,那么使用策略模式可以動態(tài)地讓一個對象在許多行為中選擇一種行為。
- 一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種。
- 如果一個對象有很多的行為,如果不用恰當(dāng)?shù)哪J?,這些行為就只好使用多重的條件選擇語句來實現(xiàn)。
- 不希望客戶端知道復(fù)雜的、與算法相關(guān)的數(shù)據(jù)結(jié)構(gòu),在具體策略類中封裝算法和相關(guān)的數(shù)據(jù)結(jié)構(gòu),提高算法的保密性與安全性。
5 總結(jié)
5.1 設(shè)計原則
- "開-閉"原則。策略模式是"開-閉"原則的一個極好的應(yīng)用范例。
- 里氏代換原則。里氏代換原則是策略模式的基礎(chǔ)。策略模式要求所有的策略對象都是可以互換的,因此它們都必須是一個抽象策略角色的子類。在客戶端則僅知道抽象策略角色類型,雖然變量的真實類型可以是任何一個具體策略角色的實例。
5.2 認(rèn)識策略模式
- 策略模式的重心
策略模式的重心不是如何實現(xiàn)算法,而是如何組織、調(diào)用這些算法,從而讓程序結(jié)構(gòu)更靈活,具有更好的維護(hù)性和擴(kuò)展性。
- 算法的平等性
策略模式一個很大的特點就是各個策略算法的平等性。對于一系列具體的策略算法,大家的地位是完全一樣的,正因為這個平等性,才能實現(xiàn)算法之間可以相互替換。所有的策略算法在實現(xiàn)上也是相互獨立的,相互之間是沒有依賴的。
所以可以這樣描述這一系列策略算法:策略算法是相同行為的不同實現(xiàn)。
- 運行時策略的唯一性
運行期間,策略模式在每一個時刻只能使用一個具體的策略實現(xiàn)對象,雖然可以動態(tài)地在不同的策略實現(xiàn)中切換,但是同時只能使用一個。
- 公有的行為
經(jīng)常見到的是,所有的具體策略類都有一些公有的行為。這時候,就應(yīng)當(dāng)把這些公有的行為放到共同的抽象策略角色Strategy類里面。當(dāng)然這時候抽象策略角色必須要用Java抽象類實現(xiàn),而不能使用接口。
這其實也是典型的將代碼向繼承等級結(jié)構(gòu)的上方集中的標(biāo)準(zhǔn)做法。

參考書籍和文章
- 《Java與模式》,電子工業(yè)出版社,閻宏
- 《設(shè)計模式——可復(fù)用面向?qū)ο筌浖幕A(chǔ)》,機(jī)械工業(yè)出版社,Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides
- 《圖說設(shè)計模式》,https://design-patterns.readthedocs.io/zh_CN/latest/index.html
- 《 < JAVA與模式 > 之策略模式》,https://www.cnblogs.com/java-my-life/archive/2012/05/10/2491891.html

