此 Java 策略模式是否具有冗余上下文类?摘要更新

2022-09-03 13:18:03

以下代码示例是从维基百科复制的策略模式的实现。我的完整问题紧随其后...

维基的方法: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

(( 编辑:在这个简单的场景中,当我省略上下文类时,我将来会犯错误吗? ))

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。通过创建不纯的策略模式,造成了混乱。我不需要重新发明轮子或试图变得更聪明(在这种情况下)。

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


答案 1

顾名思义,它封装了执行策略的点。没有这个,你只有一个裸体,调用类现在承担了额外的责任:知道何时调用它本身。你的例子可能有点太简单了,在这种特殊情况下,我会说这并没有让你太多。ContextStrategyStrategyContext

一个可能更好地说明 有用性的示例更类似于以下内容: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.
  }
}

请注意,永远不会直接从外部客户端调用 。仅使用该策略,其遵循的步骤的详细信息是一个黑匣子。这允许调整其使用策略的方式,而不会影响客户端。例如,这些步骤可以完全重新排序或调整(或完全删除)以满足性能目标或其他目标 - 但对于客户端,外部界面看起来完全相同。StrategyshipItemsContextshipItems()

还要注意,我们的例子 ,,可以根据其内部状态随时更改它。例如,如果月台太满,也许它会切换到一个更激进的调度机制,让板条箱更快地离开月台并进入卡车,从而牺牲一些效率(也许卡车没有像它们本来可以的那样有效地装载)。ContextLoadingDockLoadStrategy


答案 2

这是真实 “ ” 类在此方案中的外观的更好示例: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;
    } 
}

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


推荐