標籤:名稱 代碼結構 ati class 上下文 設計 image body 解決
引
GOF 《設計模式:可複用物件導向軟體設計的基礎》中的模式,應當算是模式歸納的鼻祖(並非他們最早發現/使用了模式,而是將已有的模式進行了整合歸納),而我們現在對於模式的學習基本上都是基於Gof的設計模式。今天主要說說行為型模式中的一個:策略模式。
模式定義
意圖:定義一系列的演算法,把它們一個個封裝起來,並且使它們可相互替換。本模式使得演算法可獨立於使用它的客戶而變化。
動機:
- 客戶程式如果直接包含策略演算法代碼,將會變得很複雜。(策略演算法本身足夠複雜)
- 不同的時候需要不同的演算法 (if....else....)
模式類圖
闡述與討論
策略模式是我們在開發中常用的模式之一。網上關於策略模式也有很多介紹,這裡再次提出,是期望給出“正規”的模式,在我初學設計模式時,也經常會被搞得不知所云,眾說紛紜還給出代碼,但幾乎都不太一樣,我也曾被策略模式搞得迷糊(主要是context),因此再次找出GOF原本進行分析- -
最常見的疑惑問題:策略模式和簡單原廠模式進行比較。兩者區別還是比較大的,不管是從語義還是實現上。
下面給出簡單工廠的類圖(GOF模式中沒有簡單工廠---只有抽象工廠和Factory 方法):
對比簡單工廠和策略模式類圖很容易發現區別: 工廠(Factory)是依賴Product介面,同時依賴於具體產品(ProductA和ProductB),而策略模式中,Context是包含了Stategy介面,同時並不依賴具體的策略類。
通過代碼很容易就可以看出區別:
策略模式代碼:
1
1 package strategydemo; 2 /** 3 * @author : xiaolu 2018年2月6日 4 * @todo : 策略上下文 5 */ 6 public class Context { 7 // 內含項目關聯性 8 private Strategy strategy ; 9 // 這個類中包含具體策略需要的其他參數,必要時可以將自己傳遞給策略類(GOF)10 public Context(Strategy strategy){11 this.strategy = strategy;12 }13 14 public int get(){15 return this.strategy.algorithm();16 }17 }
1 package strategydemo; 2 3 /** 4 * @author : xiaolu 2018年2月6日 5 * @todo : 策略介面 6 */ 7 public class StrategyB implements Strategy { 8 @Override 9 public int algorithm() {10 // 這裡的邏輯應當比較複雜,否則沒有必要使用原則類11 // 僅作說明12 return 0;13 }14 15 }
1 package strategydemo; 2 3 public class StrategyA implements Strategy { 4 @Override 5 public int algorithm() { 6 // 這裡的邏輯應當比較複雜,否則沒有必要使用原則類 7 // 僅作說明 8 return 1; 9 }10 11 }
1 package strategydemo; 2 3 /** 4 * @author : xiaolu 2018年2月6日 5 * @todo : 策略介面 6 */ 7 public class StrategyB implements Strategy { 8 @Override 9 public int algorithm() {10 // 這裡的邏輯應當比較複雜,否則沒有必要使用原則類11 // 僅作說明12 return 0;13 }14 15 }
1 package strategydemo; 2 /** 3 * @author : xiaolu 2018年2月6日 4 * @todo : 用戶端 5 */ 6 public class Main { 7 public static void main(String[] args) { 8 // 具體策略類 9 Strategy a = new StrategyA();10 //Context11 Context ctx = new Context(a);12 13 System.out.println(ctx.get());14 }15 }
代碼結構和名稱大體按照策略類圖所給出的。可以看到用戶端是“知道”(或者說依賴於)具體策略類的。而Context對於具體策略類並不存在任何依賴(無知),只是“包含”(類中屬性)了策略介面。
下面給出簡單工廠實現:
1 package simplefactory;2 3 /**4 * @author : xiaolu 2018年2月6日5 * @todo :產品介面6 */7 public interface Product {8 // ...... 原廠模式中,工廠主要用於產生產品,並不涉及產品的操作,因此此處省略具體產品的操作9 }
1 package simplefactory;2 3 /**4 * @author : xiaolu 2018年2月6日5 * @todo : 產品A6 */7 public class ProductA implements Product {8 9 }
package simplefactory;/** * @author : xiaolu 2018年2月6日 * @todo : 產品B */public class ProductB implements Product {}
1 package simplefactory; 2 3 /** 4 * @author : xiaolu 2018年2月6日 5 * @todo : 簡單工廠 6 */ 7 public class Factory { 8 // 傳回值為產品介面 9 public Product get(int i) {10 if (i == 0) {11 // 返回具體的產品(有依賴)12 return new ProductA();13 } else if (i == 1) {14 // 返回具體的產品(有依賴)15 return new ProductB();16 } else {17 throw new RuntimeException("不支援");18 }19 }20 }
1 package simplefactory; 2 3 /** 4 * @author : xiaolu 2018年2月6日 5 * @todo : 用戶端 6 */ 7 public class Main { 8 public static void main(String[] args) { 9 Factory f = new Factory();10 // 用戶端不知道(不依賴)具體產品11 Product p = f.get(1);12 // 執行p的操作13 }14 }
對比類圖和代碼實現,應該從結構上很容易區分兩者。更重要的是:策略模式是行為型模式,而原廠模式是建立型模式!通俗的說,原廠模式只負責產生“按你所需”的產品,至於你如何使用,工廠是無知的(這也是我為什麼沒有在產品介面中定義方法,因為不固定)。而策略模式中,具體的策略類是用戶端“知道”的(這也是是否需要直接使用原則模式的一個選擇【也可以規避它-策略+工廠】),而Context則負責“轉寄”(只是轉寄嗎?)這裡不妨想想,為什麼需要Context,如果沒有Context會如何呢?如果沒有:
1 Strategy strategy = new StrategyA();
2 strategy.algorithm();
好像還反而簡單了?Context是Strategy和Main(用戶端)之間的一個間接層,可以用來封裝需要傳遞給Strategy的資料結構和參數。
相對於普通的多態結構,策略模式強調的是【演算法】的可相互替代性。再次強調:如果具體策略類並不複雜就不要用策略模式(自找煩惱)。而策略類的一個弊端:用戶端類需要“知道”具體策略類------可以通過加一個工廠解決。簡單工廠的意義是什麼呢? 符合SRP,單一職能,降低耦合 ,還有一個作用---------轉換。大部分的if....else代碼我們並不能規避,只是從“這裡”移到了“那裡”,好處是代碼更明確,更清晰,更容易擴充,而簡單工廠做這種轉換非常的合適。- -
策略模式(java)