2010-01-06 92 views
13

以下代碼示例是戰略模式copied from Wikipedia的實施。我充分的問題如下它...此Java策略模式是否具有冗餘上下文類?

wiki的main方法:

//StrategyExample test application 

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); 

    } 

} 

圖案片:

// The classes that implement a concrete strategy should implement this 

// The context class uses this to call the concrete strategy 
interface Strategy { 

    int execute(int a, int b); 

} 

// Implements the algorithm using the strategy interface 
class ConcreteStrategyAdd implements Strategy { 

    public int execute(int a, int b) { 
     System.out.println("Called ConcreteStrategyA's execute()"); 
     return a + b; // Do an addition with a and b 
    } 

} 

class ConcreteStrategySubtract implements Strategy { 

    public int execute(int a, int b) { 
     System.out.println("Called ConcreteStrategyB's execute()"); 
     return a - b; // Do a subtraction with a and b 
    } 

} 

class ConcreteStrategyMultiply implements Strategy { 

    public int execute(int a, int b) { 
     System.out.println("Called ConcreteStrategyC's execute()"); 
     return a * b; // Do a multiplication with a and b 
    } 

} 

// Configured with a ConcreteStrategy object and maintains a reference to a Strategy object 
class Context { 

    private Strategy strategy; 

    // Constructor 
    public Context(Strategy strategy) { 
     this.strategy = strategy; 
    } 

    public int executeStrategy(int a, int b) { 
     return strategy.execute(a, b); 
    } 

} 

考慮特別是上面的例子,是Context級冗餘?

例如,我可以通過使用現有的類和接口除了上下文想出以下復main執行和它的工作完全一樣。它仍然鬆散耦合。

((編輯:在這個簡單的情況下,當我離開了Context類,會向我作出未來的錯誤))

public static void main(String[] args) { 

    IStrategy strategy; 

    // Three strategies 
    strategy = new ConcreteStrategyAdd(); 
    int resultA = strategy.executeStrategy(3,4); 

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

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

} 

摘要更新

以點形式列出通過回答和評論發現的內容:

  • 上下文允許改變合成策略的使用方式(例如,它的調用時間)。在調用給定策略之前和之後,不同的上下文可能會做不同的內部工作。
  • 上下文是高級別的「黑匣子」。上下文邏輯可以改變,也可以改變合成的策略(或使用不同的策略)而不會破壞客戶端,因爲客戶端只理解如何調用上下文。儘管我通過忽略上下文創建了維基百科樣例代碼的替代實現,儘管它與原始代碼一樣工作,但整個情況被簡化了(在這兩種情況下),而我的更改實際上意味着:1.它是不再是戰略模式,2.我錯過了這裏提到的戰略模式精神的好處。
  • 我的替代實現使用了像Context這樣的主要方法,所以如果有效地模擬它,我還可以保留Context。通過創建一個不純的策略模式,就產生了混淆。我不需要重新發明輪子或嘗試變得更聰明(在這種情況下)。

如果任何其他問題有用,或者需要更正請留下評論,我會相應修改列表。

回答

15

正如其名稱所暗示的,Context是什麼封裝策略執行的點。如果沒有,你只是有一個赤裸裸的Strategy,並且調用類現在拿起一個額外的責任:知道何時撥打Strategy本身。你的例子可能有點太簡單了,在這種情況下,我會說Context不會讓你太過分。

或許能更好地說明了Context的有用的例子更像是以下幾點:

public class LoadingDock { // Context. 
    private LoadStrategy ls; // Strategy. 

    public void setLoadStrategy(LoadStrategy ls) { ... } 

    // Clients of LoadingDock use this method to do the relevant work, rather 
    // than taking the responsibility of invoking the Strategy themselves. 
    public void shipItems(List<ShippingItem> l) { 
    // verify each item is properly packaged  \ 
    // ...          | This code is complex and shouldn't be 
    // verify all addresses are correct   | subsumed into consumers of LoadingDock. 
    // ...          | Using a Context here is a win because 
    // load containers onto available vehicle  | now clients don't need to know how a 
    Vehicle v = VehiclePool.fetch();  // | LoadingDock works or when to use a 
    ls.load(v, l);       // / LoadStrategy. 
    } 
} 

注意如何Strategy將永遠不會被直接從外部客戶端調用。只有shipItems使用該策略,並且它所遵循的步驟的詳細信息是黑匣子。這允許Context調整它在不影響客戶端的情況下使用策略的方式。例如,步驟可以完全重新排序或調整(或完全消除),以滿足性能目標或其他目標 - 但對於客戶的shipItems()外部接口長得一模一樣。

另請注意,我們的示例Context,LoadingDock可以根據其內部狀態隨時更改其LoadStrategy。例如,如果平臺被越來越太飽也許它會切換到這得到裝箱碼頭並裝進卡車更快更積極的調度機制,犧牲一些效率,這樣做(也許是車沒有得到儘可能有效地裝起來他們本來可以)。

+1

這是一個非常詳細的解釋,清楚地概括了上下文的重要性,包括支持代碼示例。大。 – 2010-01-06 03:34:10

+0

我想還有一個Context類可以被子類化爲將行爲附加到基本上下文工作(即在執行其額外工作之前首先回調基本上下文處理)。任何具體戰略都可以做到這一點。這將允許各部分彼此獨立地改變行爲,但仍然可以通過基本類型來兼容。所以我開始意識到,通過替代實現在水平方向上存在很大的靈活性,並且通過繼承來垂直方式,以及這些方式的組合。有趣。如果沒有經過深思熟慮,情況也會變得很複雜。 – 2010-01-06 04:11:29

+0

嗯。我不確定我喜歡多態「Context」的想法。國際海事組織,最好將不同的部分組合成一個連貫的「上下文」,然後針對每個適用的策略制定適當的策略。 – 2010-01-06 04:22:23

3

這可能是這個造型的例子,但我不會稱之爲策略的超級加超。

Context類正在演示如何簡單地通過傳入接口的新的具體實現來爲類提供不同的行爲。由於該類只知道接口,因此不需要改變。這纔是重點。不要從字面上理解其餘的例子。

你編碼它的方式將起作用,但重點是你已經將它轉換成主要方法。這不會是您通常使用策略的方式。你將在一個類中做它,而Context就是一個簡單的例子。

+0

我明白你的意思了。我正在有效地替換Context類的主要方法,這引發了爲什麼不像策略模式定義那樣只使用Context類來抽象出策略用法的問題。這對我很有幫助,因爲我可以從我想要在主要方法中「簡化」事物(例如,稍微錯誤地做)的角度思考它,並意識到這是我可以放在上下文中的東西。謝謝。 – 2010-01-06 04:21:13

+0

'我在原始問題下面添加了一個摘要更新部分以合併發現.' – 2010-01-06 05:03:07

4

這是如何真正的「Context」類可以在這個場景看起來更好的例子:

class Accumulator { 
    private Strategy strategy; 

    public Accumulator(Strategy strategy) { 
     this.strategy = strategy; 
    } 

    public int accumulate(List<Integer> values) { 
     int result = values.get(0); 
     for (int i = 1; i < values.size(); i++) { 
      result = strategy.execute(result, values.get(i)); 
     } 
     return result; 
    } 
} 

編輯:錯別字在構造固定

+0

我是否看到正確的,類名是「Accumulator」,構造函數名是「Context」?我來自C#世界,所以也許我在這裏弄錯了一些東西,錯過了你的觀點。 – 2010-01-06 04:23:31

+1

看起來像構造函數有一個錯字。 – 2010-01-06 05:04:50

0

Context不會在Strategy圖案是多餘的,它在以下情況下是有用的:

  1. 的代碼來調用特定Strategy在多個類傳播而不調用Context。未來,如果您必須重新考慮或更改界面,它將變得非常繁忙。
  2. 假設需要一些數據調用特定的策略之前,必須填充。通過提供更多信息和呼籲特定戰略的戰略方法,上下文最適合於此。

    例如Context將獲得策略和userId作爲參數。在執行Strategy之前,Context需要提供許多與用戶配置文件相關的附加信息。 Context將獲取所需信息並執行戰略的戰略方法。如果沒有上下文,如果您在100個不同的地方調用策略方法,則必須在100個不同的地方複製代碼。

  3. Context可以獨立決定哪些策略來調用。它可以根據運行時配置簡單地更改策略類型。戰略核心USP是相關算法家族之間的切換。上下文是實現它的最佳地點。

  4. 如果你有采取行動的多種策略,Context是最好的地方。axtavt提出的使用Accumulator的答案就是一個例子。

參閱這篇更多細節。

Real World Example of the Strategy Pattern