策略模式(Strategy Pattern):定義一系列算法,將每個算法封裝起來,並讓它們能夠相互替換。策略模式讓算法獨立於使用它的客戶而變化,也稱爲政策模式(Policy)。html
策略模式屬於對象的行爲模式。其用意是針對一組算法,將每個算法封裝到具備共同接口的獨立的類中,從而使得它們能夠相互替換。策略模式使得算法能夠在不影響到客戶端的狀況下發生變化。
java
這個模式涉及到三個角色:
● 環境(Context)角色:持有一個Strategy的引用。
● 抽象策略(Strategy)角色:這是一個抽象角色,一般由一個接口或抽象類實現。此角色給出 全部的具體策略類所需的接口。
● 具體策略(ConcreteStrategy)角色:包裝了相關的算法或行爲。算法
一、在有多種算法類似的狀況下,使用 if...else 所帶來的複雜和難以維護。編程
二、一個系統有許多許多類,而區分它們的只是他們直接的行爲。設計模式
假設咱們是一家新開的書店,爲了招攬顧客,咱們推出會員服務,咱們把店裏的會員分爲三種,分別是初級會員、中級會員和高級會員。針對不一樣級別的會員咱們給予不一樣的優惠。初級會員買書咱們不打折、中級會員買書咱們打九折、高級會員買書咱們打八折。ide
可是,若是咱們有一天想要把初級會員的折扣改爲9.8折怎麼辦?有一天我要推出超級會員怎麼辦?有一天我要針對中級會員可打折的書的數量作限制怎麼辦?函數
使用if\else設計出來的系統,全部的算法都寫在了一塊兒,只要有改動我就要修改整個類。咱們都知道,只要是修改代碼就有可能引入問題。爲了不這個問題,咱們可使用策略模式。測試
咱們運用策略模式來實現一下書店的收銀臺系統。咱們能夠把會員抽象成一個策略類,不一樣的會員類型是具體的策略類。不一樣策略類裏面實現了計算價格這一算法。而後經過組合的方式把會員集成到收銀臺中。this
先定義一個接口,這個接口就是抽象策略類,該接口定義了計算價格方法,具體實現方式由具體的策略類來定義。設計
public interface MemberStrategy { /** * 計算圖書的價格 * @param booksPrice 圖書的原價 * @return 計算出打折後的價格 */ public double calcPrice(double booksPrice); }
針對不一樣的會員,定義三種具體的策略類,每一個類中都分別實現計算價格方法。
/** * 初級會員 */ public class PrimaryMemberStrategy implements MemberStrategy { @Override public double calcPrice(double booksPrice) { System.out.println("對於初級會員的沒有折扣"); return booksPrice; } } /** * 中級會員,買書打九折 */ public class IntermediateMemberStrategy implements MemberStrategy { @Override public double calcPrice(double booksPrice) { System.out.println("對於中級會員的折扣爲10%"); return booksPrice * 0.9; } } /** * 高級會員,買書打八折 */ public class AdvancedMemberStrategy implements MemberStrategy { @Override public double calcPrice(double booksPrice) { System.out.println("對於高級會員的折扣爲20%"); return booksPrice * 0.8; } }
上面幾個類的定義體現了封裝變化的設計原則,不一樣會員的具體折扣方式改變不會影響到其餘的會員。
定義好了抽象策略類和具體策略類以後,咱們再來定義環境類,所謂環境類,就是集成算法的類。這個例子中就是收銀臺系統。採用組合的方式把會員集成進來。
/** *書籍價格類 */ public class Price { //持有一個具體的策略對象 private MemberStrategy strategy; /** * 構造函數,傳入一個具體的策略對象 * @param strategy 具體的策略對象 */ public Price(MemberStrategy strategy){ this.strategy = strategy; } /** * 計算圖書的價格 * @param booksPrice 圖書的原價 * @return 計算出打折後的價格 */ public double quote(double booksPrice){ return this.strategy.calcPrice(booksPrice); } }
這個Price類就是一個環境類,該類的定義體現了多用組合,少用繼承、針對接口編程,不針對實現編程兩個設計原則。因爲這裏採用了組合+接口的方式,後面咱們在推出超級會員的時候無須修改Price類。只要再定義一個SuperMemberStrategy implements MemberStrategy 就能夠了。
下面定義一個客戶端來測試一下:
public class Client { public static void main(String[] args) { //選擇並建立須要使用的策略對象 MemberStrategy strategy = new AdvancedMemberStrategy(); //建立環境 Price price = new Price(strategy); //計算價格 double quote = price.quote(300); System.out.println("圖書的最終價格爲:" + quote); } } //對於高級會員的折扣爲20% //圖書的最終價格爲:240.0
從上面的示例能夠看出,策略模式僅僅封裝算法,提供新的算法插入到已有系統中,策略模式並不決定在什麼時候使用何種算法。在什麼狀況下使用什麼算法是由客戶端決定的。
策略模式的重心
策略模式的重心不是如何實現算法,而是如何組織、調用這些算法,從而讓程序結構更靈活,具備更好的維護性和擴展性。
算法的平等性
策略模式一個很大的特色就是各個策略算法的平等性。對於一系列具體的策略算法,你們的地位是徹底同樣的,正由於這個平等性,才能實現算法之間能夠相互替換。全部的策略算法在實現上也是相互獨立的,相互之間是沒有依賴的。
因此能夠這樣描述這一系列策略算法:策略算法是相同行爲的不一樣實現。
運行時策略的惟一性
運行期間,策略模式在每個時刻只能使用一個具體的策略實現對象,雖然能夠動態地在不一樣的策略實現中切換,可是同時只能使用一個。
公有的行爲
常常見到的是,全部的具體策略類都有一些公有的行爲。這時候,就應當把這些公有的行爲放到共同的抽象策略角色Strategy類裏面。固然這時候抽象策略角色必需要用Java抽象類實現,而不能使用接口。
一、策略模式提供了對「開閉原則」的完美支持,用戶能夠在不修改原有系統的基礎上選擇算法或行爲,也能夠靈活地增長新的算法或行爲。
二、策略模式提供了管理相關的算法族的辦法。策略類的等級結構定義了一個算法或行爲族。恰當使用繼承能夠把公共的代碼移到父類裏面,從而避免代碼重複。
三、使用策略模式能夠避免使用多重條件(if-else)語句。多重條件語句不易維護,它把採起哪種算法或採起哪種行爲的邏輯與算法或行爲的邏輯混合在一塊兒,通通列在一個多重條件語句裏面,比使用繼承的辦法還要原始和落後。
一、客戶端必須知道全部的策略類,並自行決定使用哪個策略類。這就意味着客戶端必須理解這些算法的區別,以便適時選擇恰當的算法類。
二、因爲策略模式把每一個具體的策略實現都單獨封裝成爲類,若是備選的策略不少的話,那麼對象的數目就會很可觀。能夠經過使用享元模式在必定程度上減小對象的數量。
參考資料:
設計模式(十二)——策略模式
《JAVA與模式》之策略模式