15

以下代码示例是从 Wikipedia 复制的 Strategy 模式的实现。我的完整问题如下...

维基的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 之外的现有类和接口来提出以下替代实现,并且它的工作方式完全相同。它仍然是松耦合的。

((编辑:在这个简单的场景中,当我省略 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);

}

摘要更新

以点的形式列出通过答案和评论发现的内容:

  • 上下文允许组合策略的使用方式发生变化(例如,它的调用时间)。在调用给定策略之前和之后,不同的上下文可能会做不同的内部工作。
  • 上下文是一个高级的“黑匣子”。上下文逻辑可以更改,复合策略也可以更改(或使用不同的策略)而不会破坏客户端,因为客户端只了解如何调用上下文。
  • 尽管我通过省略 Context 创建了 Wikipedia 示例代码的替代实现,并且虽然它与原来的工作方式相同,但整个情况都被简化了(在这两种情况下),我的更改实际上意味着:1. 它不是一个策略模式不再,2。我想念这里提到的策略模式精神的好处。
  • 我的替代实现使用了 Context 之类的 main 方法,所以如果有效地模拟它,我不妨保留 Context。通过创建不纯的策略模式,造成了混乱。我不需要重新发明轮子或尝试变得更聪明(在这种情况下)。

如果任何其他点有用或需要更正,请发表评论,我会相应地修改列表。

4

4 回答 4

16

顾名思义,Context它封装了执行策略的点。没有它,你只有一个裸Strategy的,调用类现在承担了一个额外的责任:知道什么时候调用它Strategy自己。您的示例可能有点太简单了,在这种特殊情况下,我会说这Context并没有让您得到太多。

一个可能更好地说明 a 有用性的示例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例如,如果码头太满,它可能会切换到更积极的调度机制,更快地将板条箱从码头运到卡车上,这样做会牺牲一些效率(也许卡车的装载效率不如他们本来可以)。

于 2010-01-06T02:58:45.530 回答
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;
    } 
}

编辑:构造函数中的错字已修复

于 2010-01-06T02:58:02.800 回答
3

可能是为了这个虚构的例子,但我不会把它称为策略的 ne plus ultra。

Context 类演示了如何简单地通过传入接口的新具体实现来赋予类不同的行为。由于类只知道接口,因此无需更改任何内容。这才是重点。不要太从字面上理解这个例子的其余部分。

您编码它的方式将起作用,但关键是您已将其转储到 main 方法中。这不是您通常使用策略的方式。您将在课堂上进行,而 Context 就是一个简单的例子。

于 2010-01-06T02:56:04.990 回答
0

Context在模式上不会是多余的Strategy,它在以下场景中很有用:

  1. 调用特定代码的代码Strategy分布在多个类中,而无需调用Context. 将来,如果您必须重构或更改Strategy界面,那将是一项繁重的任务。

  2. 假设在调用特定策略之前需要填充一些数据。通过提供附加信息并调用特定策略的策略方法,上下文最适合此处。

    例如Context,将获得一个 Strategy 和 userId 作为参数。在执行之前StrategyContext需要提供很多与 User Profile 相关的附加信息。Context将获取所需的信息并执行 Strategy 的策略方法。在没有上下文的情况下,如果您在 100 个不同的地方调用策略方法,则必须在 100 个不同的地方复制代码。

  3. Context可以独立决定调用哪个策略。它可以根据运行时配置简单地更改策略类型。策略核心 USP 是在相关算法家族之间切换。上下文是实现它的最佳场所。

  4. 如果你必须对多个 Strategies 采取行动,Context是最好的地方。axtavt 提出的使用答案Accumulator就是一个例子。

参考这篇文章更多细节。

策略模式的真实示例

于 2016-06-03T12:07:14.763 回答