策略模式用於演算法的自由切換和擴展,實現了演算法定義和演算法分離的使用 模式動機 要完成一項任務,可以有多種不同的方式,例如人們外出旅游時可以選擇多種不同的出行方式,如自行車、坐汽車、坐高鐵或乘飛機等,每一種方式稱為一個策略,我們可以根據環境或者條件的不同選擇不同的策略來完成該任務。 在實際的軟體開發中, ...
策略模式用於演算法的自由切換和擴展,實現了演算法定義和演算法分離的使用
模式動機
要完成一項任務,可以有多種不同的方式,例如人們外出旅游時可以選擇多種不同的出行方式,如自行車、坐汽車、坐高鐵或乘飛機等,每一種方式稱為一個策略,我們可以根據環境或者條件的不同選擇不同的策略來完成該任務。
在實際的軟體開發中,一項功能也有很多演算法可以實現,如果我們直接把多種演算法集中在一個類,或者說使用條件判斷語句來進行選擇,無疑會增加代碼複雜性,不利於維護。
為瞭解決這些問題,可以定義一些獨立的類來封裝不同的演算法,每一類封裝一個具體的演算法,將每一個封裝演算法的類稱之為策略(Strategy)。為了保證策略的一致性,一般會用一個抽象的策略類來做演算法的定義,每種具體演算法對應於一個具體策略類。
模式定義
策略模式(Strategy Pattern)定義一系列演算法,將每個演算法封裝起來,並讓它們可以相互替換。策略模式讓演算法獨立於使用它的客戶而變化,也稱政策模式(Policy)。策略模式是一種對象行為模式。
Define a famliy of algorithms, encapsulate each one,and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
模式分析
說到策略模式就不得不提一下狀態模式了,一個是對狀態的封裝,另一個則是對演算法的封裝,因此實現方式上兩者有許多共同點,理解起來並不複雜
策略模式是對演算法的封裝,把演算法的責任和演算法本身分割開來,委托給不同對象管理。策略模式通常把一系列演算法封裝到一系列的策略類裡面,作為一個抽象策略類的子類。
由此可以得出結構類圖如下:
環境類(Context)是需要使用演算法的對象,它在解決某個問題時可以採用多種策略,在環境類中維護一個對抽象策略類的引用實例,用於定義所採用的策略。假如我們不使用策略模式,可能會存在如下代碼:
public class Context {
...
public void algorithm(String type) {
...
if(type == "strategyA") {
// 演算法A
} else if(type == "strategyB") {
// 演算法B
} else if(type == "strategyC") {
// 演算法C
}
...
}
...
}
客戶端要調用 Context 類的 algorithm() 方法時,需要根據所傳入的參數來選擇具體演算法,這將導致代碼過於龐大,不利於維護。而且最大的問題是,在環境類中定義演算法,如果需要修改或增加演算法,則勢必要修改源代碼,違反了開閉原則。
導致以上問題的主要原因在於環境類職責過重,即違背了單一職責原則,策略模式正是解決這個問題的好幫手。引入一個抽象策略類(Strategy),在其中定義抽象演算法,每一個繼承它的具體策略類(ConcreteStrategy)使用具體演算法實現某個業務辦理。環境類只針對抽象策略類編程,符合依賴倒轉原則,出現新的演算法時,只需要增加一個新的實現了抽象策略類的具體策略類即可。
public abstract class AbstractStrategy {
public abstract void algorithm();
}
將每一種具體演算法作為該抽象策略類的子類
public class ConcreteStrategyA extends AbstractStrategy {
public void algorithm() {
// 演算法A
}
}
對於環境類,在它與抽象策略類之間建立一個關聯關係
public class Context {
private AbstractStrategy strategy;
public void setStrategy(AbstractStrategy strategy) {
this.strategy = strategy;
}
public void algorithm() {
strategy.algorithm();
}
}
對於用戶而言,使用環境類時只需註入一個具體策略對象即可。用戶可以靈活更換具體策略類,比如使用配置文件,增加新的具體策略類也很方便,因此策略模式相當於“即插即用的演算法”。
需要註意的是,策略模式並不負責“哪一個具體策略類適用於哪一種情況”這個決定,換言之,應當由客戶決定在什麼情況下使用什麼具體策略角色,一定程度上提高了系統靈活性,但前提是客戶需要理解所以具體策略類之間的區別及最佳使用場景。
對比狀態模式,狀態模式可以實現自主的狀態切換,並根據狀態的不同做出不同的行為。而
模式優缺點
策略模式的優點:
- 策略模式提供了對開閉原則的完美支持,將演算法的使用與實現完美分離,用戶可以在不修改原有系統的基礎上選擇和新增演算法
- 策略模式的等級結構定義了一個演算法族,恰當使用繼承可以把公共的代碼移到父類裡面,避免重覆代碼
- 可以避免使用多重條件轉移語句
策略模式的缺點:
- 客戶端必須知道所有的策略類,並自行決定使用哪一種演算法
- 策略模式將產生很多策略類和對象,可以通過享元模式在一定程度上減少對象的數量
策略模式與狀態模式的比較
由於結構的相似性,所以很容易會把策略模式和狀態模式混餚,但它們是為解決不同的問題而設計的,是完全不同的兩種模式。如何區分是策略模式還是狀態模式?其區別如下:
- 如果環境角色存在多種狀態,而且這些狀態之間可以進行轉換,則應使用狀態模式,在狀態模式在,環境類在生命周期中,會有一個不同的狀態對象被創建和使用;如果環境角色只有一個狀態,那麼應當使用策略模式,因為一旦環境角色選擇了一個具體策略類,那麼在整個環境類的生命周期里它都不會改變這個具體策略類
- 策略模式的環境類自己選擇一個具體策略類,具體策略類無須關心環境類;狀態模式的環境類與狀態類之間存在一種雙向關係,以便實現狀態的切換
- 使用策略模式,客戶端需要知道所選的具體策略模式是哪一個,而使用狀態模式,客戶端無須關心具體狀態,環境類的狀態會根據用戶的操作自動轉換
- 如果系統中某個類的對象存在多種狀態,不同狀態下行為有差異,而且這些狀態之間可以發送轉換時使用狀態模式;如果某個類的行為存在多種實現方式,而且這些方式可以互換時使用策略模式