这个 Java 策略模式是否有多余的 Context 类?

发布于 2024-08-17 09:44:34 字数 3172 浏览 2 评论 0原文

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

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 类是多余的吗?

例如,我可以通过使用除 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 这样的 main 方法,所以如果可以有效地模拟它,我最好保留 Context。通过创建不纯粹的策略模式,造成了混乱。我不需要重新发明轮子或尝试变得更聪明(在这种情况下)。

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

The following code sample is an implementation of the Strategy pattern copied from Wikipedia. My full question follows it...

The Wiki's main method:

//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 pattern pieces:

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

}

Considering specifically the above example, is the Context class redundant?

For example, I can come up with the following alternate main implementation by using the existing classes and interface except Context and it will work exactly the same. It is still loosely coupled.

(( Edit: In this simple scenario, when I leave out the Context class, will I be making a future mistake? ))

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

}

Summary Update

Listing in point form what was discovered through answers and comments:

  • The Context allows for variation in how the composited Strategy is used (e.g. timing of it's call). Different Contexts might do different internal work before and after calling the given Strategy.
  • The context is a high level "black box". The Context logic can change, also the composited Strategy can change (or a different one used) without breaking the client because the client understands only how to call the context.
  • Even though I created an alternate implementation of the Wikipedia sample code by leaving out the Context, and although it worked the same as the original, the entire situation was simplified (in both cases) and my changes actually meant: 1. it's not a Strategy pattern anymore, 2. I miss the benefits of the spirit of the Strategy pattern that are mentioned here.
  • My alternate implementation used the main method like a Context, so I might as well keep the Context if effectively simulating it. By creating an impure Strategy pattern, confusion was created. I didn't need to reinvent the wheel or try to be smarter (in this case).

If any other points would be useful or if this needs correction leave a comment and I'll modify the list accordingly.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

ぺ禁宫浮华殁 2024-08-24 09:44:34

顾名思义,Context 封装了策略执行的点。如果没有它,您就只有一个赤裸裸的策略,而调用类现在承担了额外的责任:知道何时调用策略本身。您的示例可能有点太简单了,在这种特殊情况下,我想说 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。例如,如果码头太满,它可能会切换到更积极的调度机制,更快地将板条箱从码头转移到卡车上,从而牺牲一些效率(也许卡车的装载效率不如卡车)他们本可以是)。

As the name suggests, the Context is what encapsulates the point at which the strategy is performed. Without that, you just have a naked Strategy, and the calling class now picks up an extra responsibility: knowing when to call the Strategy itself. Your example is perhaps a bit too simple, and in this particular case, I would say that the Context isn't getting you too much.

An example that perhaps better illustrates the usefulness of a Context is more like the following:

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.
  }
}

Notice how the Strategy will never be called directly from an external client. Only shipItems uses the strategy, and the details of the steps it follows are a black box. This allows the Context to adjust how it uses the strategy without affecting clients. For instance, the steps could be completely reordered or adjusted (or removed entirely) to meet performance objectives or other goals -- but for the client, the external interface of shipItems() looks exactly the same.

Notice, also, that our example Context, the LoadingDock, could change its LoadStrategy at any time based on its internal state. For example, if the dock is getting too full perhaps it will switch to a more aggressive scheduling mechanism that gets crates off the dock and into trucks faster, sacrificing some efficiency in doing so (maybe the trucks don't get loaded up as efficiently as they could have been).

余生共白头 2024-08-24 09:44:34

这是真正的“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;
    } 
}

编辑:修复了构造函数中的拼写错误

This is the better example of how the real "Context " class can look in this scenario:

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

EDIT: Typo in constructor fixed

难理解 2024-08-24 09:44:34

这可能是为了这个虚构的例子,但我不会称其为策略的最佳选择。

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

您编码的方式可以工作,但重点是您已将其转储到 main 方法中。这不会是您通常使用策略的方式。您将在一个类中执行此操作,Context 就是一个简单的示例。

It might be for this made-up example, but then I wouldn't call this the ne plus ultra of Strategy.

The Context class is demonstrating how you can give a class different behavior simply by passing in a new concrete implementation of the interface. Since the class only knows the interface, nothing has to change. That's the point. Don't take the rest of the example too literally.

The way you coded it will work, but the point is that you've dumped this into a main method. That won't be the way you'll typically use Strategy. You'll be doing it within a class, and Context is a simple example of that.

温柔一刀 2024-08-24 09:44:34

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

  1. 调用特定 Strategy 的代码是分散的在多个类中而不调用 Context 。将来,如果您必须重构或更改Strategy接口,这将是一项艰巨的任务。

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

    例如Context将获取策略和userId作为参数。在执行Strategy之前,Context需要提供大量与User Profile相关的附加信息。 Context将获取所需的信息并执行Strategy的策略方法。在没有Context的情况下,如果你在100个不同的地方调用策略方法,你就必须在100个不同的地方重复代码。

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

  4. 如果您必须执行多个策略,Context 是最好的地方。 axtavt 提出的使用 Accumulator 的答案就是一个例子。

请参阅这篇文章更多详细信息。

策略模式的真实示例

Context won't be redundant in Strategy pattern and it is useful in below scenarios:

  1. The code to call a particular Strategy is spread in multiple classes without invoking Context . In future, if you have to re-factor or change the Strategy interface, it will be come a hectic task.

  2. Assume that some data need to be populated before calling a particular Strategy. Context is best fit here by providing additional information and call Strategic method of a particular Strategy.

    e.g. Context will get an Strategy and userId as parameter. Before executing Strategy, Context need to provide lot of additional information related to User Profile. Context will fetch the required information and executes strategic method of Strategy. In absence of Context, you have to duplicate the code at 100 different places if you call strategic method at 100 different places.

  3. Context can take independent decision on which Strategy to invoke. It can simple change the Strategy type depending on run time configuration. Strategy core USP is switching between family of related algorithm. Context is the best place to achieve it.

  4. If you have to act on multiple Strategies , Context is the best place. axtavt proposed answer of using Accumulator is one example.

Refer to this post more details.

Real World Example of the Strategy Pattern

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文