這個模式,怎么說呢,聽名字真的很唬人,但是實際上,就是簡單地用一個接口進行方法的抽象......
但是關鍵是這個模式想要傳達的思想:
在制定策略時,將策略的具體實現與調用者解耦,且策略間可靈活互換。
我們還是來看一下設計模式一書是怎么描述策略模式的意圖的:
Define a family of algorithms, encapsulate each one, and make theminterchangeable. Strategy lets the algorithm vary independently fromclients that use it.
所以策略模式要解決的是一個思維上的抽象過程,而非提出一個具體的且靈性的解決方案(solution)。
大家看這個類圖其實很簡單,三個類實現一個接口,調用者使用接口中定義的方法。
關于策略模式其實沒有特殊的需要說明的,但是它可以被用來進行除了上一篇模板方法模式提到的之外,另一種鉤子方法的實現方式。
我們在Context中需要定義一個Strategy的變量,但是試想我們將變量的具體類放在調用時再確定,這個時候我們應該如何實例化strategy呢?這個時候我們需要一個空對象(Null Object)。
//Null Object
public class NullStrategy{
public void strategyInterface(){
//do nothing...
}
}
而在Context里的實現中,我們用NullStrategy來實例化strategy:
public class Context{
private Strategy strategy = new NullStrategy();
public Context(){}
public void contextInterface(){
strategy.strategyInterface();
}
public void setStrategy(Strategy s){
this.strategy = s;
}
}
由于有了setStrategy這個方法,我們可以將最終確定實現策略的子類確定時間延后到使用時刻。
public static void main(String[] args){
Context context = new Context();
context.contextInterface(); //do nothing...
context.setStrategy(new ConcreteStrategyB());
context.contextInterface(); //do ConcreteStrategyB.strategyInterface()
}
而這個setStrategy則成為了我們在代碼中留出的一個鉤子,增強了類的靈活性。
另外在類中使用一個不做任何事的子類來進行實例化,其實也算是一種設計模式,只不過沒有在GoF的書中提到。
附:Null Object模式
在設計實現中,很多地方都用到了Null Object設計模式。 Null Object提供了“什么也不做”的行為,隱藏來自它的合作者的細節。對于如何理解和應用該模式,通過一個實例就能很好的進行說明。這一節我們在討論消息分派器,消息分派器使用了前述的日志記錄器,并且通過屬性來注入具體的日志記錄器對象。
private ILogger logger;
public void setLogger(ILogger value){
this.logger = value;
}
現在假設,我們在消息分派器內部的多個地方使用日志記錄器來進行日志記錄,我們總要寫這樣的語句:
if (logger != null){
logger.Log(); //記錄日志
}
也就是說,在使用之前,我們都要判斷一下日志記錄器的引用是否為空,如果不為空才可以調用其Log方法。如果調用日志記錄器進行日志記錄的地方很多,那么每個地方都會充斥著這種判斷其引用是否為空的代碼。有沒有辦法來避免這所有的判斷語句了,有!那就是使用Null Object設計模式。
為每種必要的組件都提供了對應的Null Object類型,這些類型的名字以“Empty”作為前綴。比如ILogger對應的Null Object類型就是EmptyLogger,EmptyLogger實現的Log方法什么也不用做:
public void Log(string errorType ,string msg, string location, ErrorLevel level){
//Do Nothing !
}
有了EmptyLogger,我們就可以象這樣來設計消息分派器的日志記錄器屬性:
private ILogger logger = new EmptyLogger();
public void setLogger(ILogger value){
if (value != null){
logger = value;
}
}
如此一來,在消息分配器內部,我們就可以非常方便的直接使用日志記錄器,而不用再判斷其引用是否為空,因為無論如何,它總是指向一個有效的對象,即使這個對象是Null Object。