2009-11-10 50 views
22

什麼時候會使用Strategy Pattern什麼時候和爲什麼要使用戰略模式?

我看到客戶端代碼片段是這樣的:

 

class StrategyExample { 

    public static void main(String[] args) { 

     Context context; 

     // Three contexts following different strategies 
     context = new Context(new ConcreteStrategyAdd()); 
     int resultA = context.executeStrategy(3,4); 

     context = new Context(new ConcreteStrategySubtract()); 
     int resultB = context.executeStrategy(3,4); 

     context = new Context(new ConcreteStrategyMultiply()); 
     int resultC = context.executeStrategy(3,4); 

    } 

} 
 

,它看起來像你可以只是把它重構爲這樣:

 

class StrategyExample { 

    public static void main(String[] args) { 
     // Three contexts following different strategies 
     int resultA =new ConcreteStrategyAdd().execute(3,4); 
     int resultB =new ConcreteStrategySubtract().execute(3,4); 
     int resultC =new ConcreteStrategyMultiply().execute(3,4); 
    } 

} 
 

的第一部分代碼是直接從維基百科頁面取。一個很大的區別是環境消失了,但它在這個例子中並沒有做任何事情。也許有人有一個更好的例子,戰略是有道理的。我通常喜歡設計模式,但這似乎增加了複雜性而不增加實用性。

+0

http://www.youtube.com/watch?v=MOEsKHqLiBM – BrunoLM 2014-09-06 02:53:05

回答

47

玩具例子的問題在於,它很容易錯過這一點。在這種情況下,代碼確實可以像您所示的那樣被實施。在戰略模式中,主要價值在於能夠針對不同情況切換不同的實施方式。

您擁有的示例僅說明模式中的對象以及它們之間的交互。想象一下,你有一個組件可以爲網站呈現圖形,這取決於它是一個真正的網頁瀏覽器還是另一端的智能手機,你會有一些代碼來檢測創建的​​瀏覽器類型,並在另一個設置策略組件可以使用策略對象中的一些複雜的代碼,將不需要複製,並會盡在這兩種情況下離開圖的實際繪圖的細節,以適當的策略對象的工作:

interface GraphStrategy { 
    Image renderGraph(Data graphData); 
} 

class BigGraphStratedy implements GraphStrategy { 
    ... 
} 

class SmallGraphStrategy implements GraphStrategy { 
    ... 
} 

然後在其他一些代碼中:

GraphStrategy graphStrategy; 

if (phoneBrowser == true) { 
    graphStrategy = new SmallGraphStrategy(); 
} else { 
    graphStrategy = new BigGraphStrategy(); 
} 

其餘的應用程序代碼可以使用而不必知道是否正在執行完整或小型圖像渲染。

+0

這就是我一直在尋找的。謝謝! – User1 2010-01-03 20:43:15

+0

我們可以注入依賴與彈簧,如果其他部分? – Harshana 2015-06-26 17:41:26

+0

當我們選擇正確的類的策略出現在switch語句中時,它叫做策略模式令我困擾。這更像是「界面策略」,因爲它真的只是界面的一個練習。你懂!?很好的例子。 – 2017-01-23 11:34:56

3

您引用的代碼片段有點欺騙性,因爲它稍微偏離了上下文。在你的例子中,你在下面寫的是戰略模式 - 你剛纔更簡潔地重寫了上面的例子。

該示例的要點是,數學運算的細節從調用者中抽象出來。這樣,調用者可以通過創建一個新的ConcreteStrategy與任何二元運算符一起工作,例如,

int mod = new ConcreteStrategy(){ 
     public int execute(int a, int b){ return a %b; } 
    }.execute(3,4); 
8

領域浮現在腦海:

  • 資源分配器。在手動資源管理中,這可能會最小化資源分配所需的時間或最小化碎片。這裏的每個策略都有一個具有相同接口的「分配」方法,用戶根據他們正在嘗試優化的內容,決定使用哪種策略。
  • 一種連接和發送網絡數據的方法。也許在某些情況下,您更願意連接併發送UDP數據報,也許在其他情況下,性能不如使用TCP/IP發送的因素。
  • 數據格式化/序列化策略。允許代碼決定是否應該用Json或Xml序列化一個對象。也許一個用於機器,另一個用於人類可讀的情況。兩種策略都有一個「序列化」方法,它需要一個對象。每個序列化不同。

主題是,決定是否以某種方式做某事取決於情境因素,而您或您的代碼會根據情況選擇正確的策略。

現在爲什麼會變成這樣不是像更多有用:

void DoIt() 
{ 
    if (... situation1...) 
    { 
     DoA() 
    } 
    else 
    { 
     DoB(); 
    } 
} 

的原因是有時你只想做決定一次,忘掉它。策略模式的另一個重要主題是,您需要從需要執行策略的代碼中分離出使用哪種策略的決策。

DoItStrategy MakeDoItStrategy() 
{ 
    if (... situation1...) 
    { 
      return new DoItStrategyA(); 
    } 
    else 
    { 
      return new DoItStrategyB(); 
    } 
} 

在最後一個示例中,您可以只存儲該策略,將其作爲實現策略接口的另一個對象傳遞。對於那些執行策略的人來說,他們只是有一種方法來執行一個動作。他們不知道內部工作是什麼,只是界面會得到滿足。策略的使用者不需要知道我們爲什麼做出決定。他們只需要採取行動。我們做出一次決定,並將策略傳遞給使用策略的類。

例如,考慮一種情況,我們根據給定的網絡配置制定一個程序範圍的決定,用UDP連接和發送數據到遠程主機。而不是網絡接口的每個用戶需要知道決策的邏輯(上面的「DoIt」功能),我們可以預先創建UDP策略並將其傳遞給需要發送網絡數據的每個人。然後這個策略實現了一個簡單的界面,並且具有相同的最終結果 - 數據從A到B.

0

我通常根據情況使用戰略模式,當我有一堆不同的事情要做時。實質上,它是將一系列if/else語句轉換爲幾行的一種方式。要做到這一點(在Java中)

方式一:

Map<String, Strategy> strategyMap = new HashMap<String, Strategy>(); 
strategyMap.put("bark", new BarkingStrategy()); 
strategyMap.put("meow", new MeowingStrategy()); 
strategyMap.put("moo", new MooingStrategy()); 
strategyMap.put("giraffeSound", new UnknownStrategy()); 

你先建立某種形式的戰略劇目。

後來......

String command = //...some form of input 
strategyMap.get(command).execute(); 

這種方式,你可以在 「一般」 處理許多不同的情況。

即:

moo 

將執行MooingStrategy()

0

Context知道如何做一些複雜的事情,給予一定的操作,您提供給它。操作很簡單(添加兩個數字,乘以兩個數字等)。在一個非平凡的例子中,一個Context可能需要許多不同的行爲(不僅僅是一個行爲),這些行爲最好被分解爲「策略」(因爲嘗試子類Context會創建子類的組合爆炸)。

2

是的,這個例子是蹩腳的,但具有不同實現策略的委託的概念是一個基本的,非常古老的設計模式,我已經在許多應用程序中使用過。

我認爲你的問題在這裏很常見,但我幾乎看到的每一個設計模式例子都是令人難以置信的設計,總是會讓我像你一樣提出問題 - 當用這種方式呈現時,他們總是看起來毫無用處。

0

當上下文對象具有更多職責並且策略抽象將這些職責從操作的某個方面解耦時,這會更有意義。一個例子,(在C#中)是IComparer接口:

interface IComparer<T> 
{ 
    int Compare(T a, T b); 
} 

可以將其傳遞給排序算法。

2

Mac和iPhone上使用的Cocoa框架使用策略模式a lot,不同之處在於我們將其稱爲委託模式。以下是我們如何使用它:

我們有一個具體的對象,說一個NSTableView。表視圖需要知道它有多少行,每行,每列有什麼內容。因此,我們不提供子視圖來提供這些信息,而是提供一個「委託」對象。該委託對象實現了一個特定的接口(Objective-C中的「協議」)。然後,tableview可以簡單地詢問它的委託對象在某些情況下應該做些什麼(「我有多少行?」「這個單元格中有什麼?」「用戶是否允許選擇這一行?」)。我們可以在運行時簡單地通過分配一個符合NSTableViewDelegate協議的新對象來交換委託對象。

所以是的,戰略模式是我的最愛之一,我使用每一天。

0

主要區別在於在第二個例子中,策略是算法(因此沒有模式)。 在第一個示例中,您正在對算法的一部分進行抽象/隔離。

例如,Context.executeStrategy()實現可能是:

public int executeStrategy(int baseValue, int exponentFactor) 
{ 
    return (int) Math.Pow(baseValue, this.Strategy.Calculate(2, exponentFactor)); 
} 
1

策略模式是在您(或您的代碼的用戶)可能要改變計算在你的算法非常有用。我已經使用了戰略模式的一個簡單例子是在A *搜索中對啓發式進行建模。 A *使用啓發式算法,如果在目標節點(Ng)的路徑上選擇某個節點(Ni),則這些算法是簡單的計算來估算剩餘成本。我看着界面是這樣的:

class Heuristic { 
public: 
    virtual int estimateRemainingCost(const node &Ni, const node &Ng) const = 0; 
}; 

而且他們使用的是這樣的:

... 
// for each node (Ni) that is adjacent to the current node Nc 
int node_priority = cost(Ni)/* the cost of choosing this node on the path */ 
        + heuristic->estimateRemainingCost(Ni, Ng); 
unsearched_nodes_.queue(node_priority, Ni); 
... 

啓發式是可以更換的策略或計算。換句話說,改變搜索算法的啓發式算法是一項簡單的練習。

相關問題