以下代码示例是策略模式copied from 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);
}
以点的形式列出通过答案和评论发现的内容:
如果任何其他要点有用或者如果需要更正请留下评论,我会相应地修改列表。
答案 0 :(得分:16)
顾名思义,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 :(得分: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;
}
}
编辑:构造函数中的错误已修复
答案 2 :(得分:3)
这可能是为了这个例子,但我不会把它称为战略的超级优势。
Context类演示了如何通过传递接口的新具体实现来为类提供不同的行为。由于类只知道接口,所以不必改变。这才是重点。不要太过字面意思地采用其余的例子。
编码它的方式可行,但重点是你已将其转换为main方法。这不是您通常使用策略的方式。你将在一个类中完成它,Context就是一个简单的例子。
答案 3 :(得分:0)
Context
在Strategy
模式中不会多余,在以下情况下非常有用:
Strategy
的代码分散在多个类中,而不调用Context
。将来,如果您必须重新考虑或更改Strategy
界面,那么这将是一项艰巨的任务。假设在调用特定策略之前需要填充一些数据。通过提供额外信息并调用特定策略的战略方法,上下文最适合此处。
e.g。 Context
将获取策略和userId作为参数。在执行Strategy
之前,Context
需要提供与用户个人资料相关的大量其他信息。 Context
将获取所需信息并执行策略的战略方法。如果没有Context,如果在100个不同的地方调用策略方法,则必须在100个不同的地方复制代码。
Context
可以独立决定要调用哪个策略。它可以根据运行时配置简单地更改策略类型。策略核心USP是在相关算法族之间切换。上下文是实现它的最佳场所。
如果您必须采取多种策略,Context
是最佳选择。 axtavt建议使用Accumulator
的答案就是一个例子。
请参阅此帖更多详情。