對于工廠、策略、命令、適配、模版方法等模式大家應該都非常清楚了,因為這是使用極其頻繁的一些模式。如果對于設計模式不是很清楚,估計也應該是在無形中使用了。剛review一塊代碼,想起和策略模式使用的時候的幾個問題,僅供大家參考。
說明:本人對設計模式理解很有限,工作經驗也不長,歡迎指正,謝絕諷刺、挖苦^_^
【策略模式的本質】
如果先來無事想一下經典的23個OO設計模式,部分其實就是在良好地利用繼承,部分則是避免繼承,當然是否使用繼承是由技術需求決定的。“繼承封裝變化”這句話聽的耳朵都起繭子了,繼承是多態的結構基礎,多態特性給予了針對不同實例對同一請求做出不同反應的能力。我們在遇到可變的點時候,一般處理如下:
1、對可變點抽象出一個共性的概念,用繼承封裝這種變化
2、在客戶端以引用的方式使用這種概念
很顯然,上面1的結果是產生抽象類型(往往是對外暴露,基于接口),并繼承產生一系列擴展實現(往往是作為擴展實現,不一定對外暴露),上面2則需要我們思考如何構造實例,推薦的是講類型本身和類型實例的構造進行分離(是客戶端自己構造,還是有個工廠類型的角色來負責這種構造?如果有個工廠的角色來承擔實例化的任務,那么這些擴展實現則跟容易隱藏)。
『PS:我們常說的基于接口編程,但是接口本身必然是不能實例化的,那么是用戶訪問具體實現類型構造函數來實例化呢,還是從一個工廠中去取呢? 當然,理論上后者更好,也是基于接口編程的重要保證手段!』
我們的策略模式精髓是:將算法(特定類型行為邏輯)封裝進對象,便于獨立使用。廣義的講,處理上面說的變化的點的手段有很多種,策略模式是其中之一,設計模式是工具層面^_^ (需求中變化的點如何處理,策略模式可能用的上,絕對不是想用策略模式,去產生一些變化的點)。但是,使用策略模式的經驗告訴我們,定義的算法接口經常要修改...
【策略模式使用時候的幾個疑問】
先說幾個疑問:1、數據上下文合理嗎;2、策略本身是否需要實例狀態;3、如何實例化和管理這些策略;4、行為控制上下文如何處理
場景:構建一個電子商務系統,要處理不同地區的業務,地區不同業務處理規則可能有點區別。
很自然的,我們剛開始定義如下的接口(就叫做IPolicy吧 ~_~):
1 public interface IPolicy {
2 public boolean process(String id, String account
);
3 }
上面的id、account等參數就是我們處理一個訂單需要的參數,也是從一個請求(Requst)數據結構中的部分信息。到這里,感覺一起OK。
數據上下文合理呢?
這個疑問出來,才回想起來,在定義IPolicy接口的時候,對process操作接受的數據上下文并沒有進行足夠的分析。可能就是假借一個地區的訂單處理想了一下,處理一個訂單需要這些信息。
隨著新地區訂單策略實現的加入,最有可能帶來的一個問題就是:
你的process給的參數信息不夠!!!一旦不夠,那么你要修改的就是接口定義了,這個時候可能已經有一些策略實現了,修改吧!!!
對IPolicy接口修改如下:
1 public interface IPolicy {
2 public boolean process(Request request);
3 }
這里的Request是從系統接受到的請求(假設是從一個web service過來的,上面的id、account就是從Request中獲取來的),那么Request里面的信息一般來說對于處理一個請求是足夠的了, 這樣你定義的IPolicy就更能夠應對數據上下文的變化了。
如何實例化和管理這些策略?誰承擔這個責任?
假設,有了兩個實現,分別針對A區域和B區域:
1 public class RegionAPolicy implements IPolicy {
2
3 public boolean process(Request request) {
4 //
5 return true;
6 }
7 }
1 public class RegionBPolicy implements IPolicy {
2
3 public boolean process(Request request) {
4 //
5 return true;
6 }
7 }
假設,現在客戶端如下使用:
1 //
2 String region = requst.getRegion();
3 if ("A".equalsIgnoreCase(region)) {
4 IPolicy policy = new RegionAPolicy();
5 boolean result = policy.process(request);
6 //
7 }
8 else if ("B".equalsIgnoreCase(region)) {
9 IPolicy policy = new RegionBPolicy();
10 boolean result = policy.process(request);
11 //
12 }
13 //
客戶端首先獲取對應的region信息,然后if else去創建對應的策略實例,然后再去調用這些實例上的服務.......那如果有個中間角色來管理這些策略,是不是能夠把類型的使用和類型的實例化分開呢?引入一個管理器角色看一下:
1 public class PolicyManager {
2 private PolicyManager() {}
3
4 public static PolicyManager getInstance() {//
}
5
6 public IPolicy getPolicy(String region) {//
}
7
8 }
客戶端調用代碼變為如下:
1 //
2 String region = requst.getRegion();
3 IPolicy policy = PolicyManager.getInstance().getPolicy(region);
4 boolean result = policy.process(request);
5 //
比較前面的調用代碼,我們發現,客戶端少干了一件事情:實例化和管理實例!!! 而且這樣也可以做到具體的Policy實現類型對客戶端隱藏,客戶端真正基于接口。上面的PolicyManager我并沒有具體寫如何管理實例,在本問題場景下,只要保證一點就可以了,用戶找你這個manager去獲取指定region的策略。~_~
策略對象需要實例狀態嗎?可以當作單態處理嗎?需要同步化嗎?
需要分析你的策略,準確地判斷你的策略實例是否需要實例字段來表示狀態。一般是不需要的~_~ 如果引入實例變量,你的策略要有相應的管理...
如果可以認為是單太的,那么同步化...
控制上下文如何設計...?
你的策略如何面對異常情況?例如發生異常時怎么辦?異常信息又如何處理?是否可以把異常信息都收集起來呢,這樣可以針對這些異常信息做各種處理 等等
例如,可能將接口調整如下:
1 public interface IMessageCollector {
2 public void reportError(
);
3
4 public void reportWarning(
);
5 }
1 public interface IPolicy {
2 public boolean process(Object request, IMessageCollector collector);
3 }
1 //
2 IMessageCollector messageCollector = new MessageCollectorImpl();
3
4 String region = requst.getRegion();
5 IPolicy policy = PolicyManager.getInstance().getPolicy(region);
6 boolean result = policy.process(request, messageCollector);
7
8 if (!result) {
9 //TODO:處理messageCollector的一些錯誤和警告信息等
10 }
11 //
這樣我們的異常信息的就可以集中管理,可以在客戶端做各種處理,而不需要修改策略本身,加強策略本身的復用。
后記
既然我們使用策略來封裝策略變化,那么我們應該也要思考如何讓我們的策略減少修改、易于擴展。其實如何將設計模式和一些基本的OO設計原則合理配合,在原則的指導下去用設計模式這個工具...是個很大的話題,不知道怎么去說。
以上寫的是一點自己的感想,大家有什么好的意見,敬請分享一下。文中的示意代碼僅僅是為了輔助說明問題~_~
本博客中的所有文章、隨筆除了標題中含有引用或者轉載字樣的,其他均為原創。轉載請注明出處,謝謝!