策略模式是一种行为设计模式, 它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。
策略模式建议找出负责用许多不同方式完成特定任务的类, 然后将其中的算法抽取到一组被称为策略的独立类中。
名为上下文的原始类必须包含一个成员变量来存储对于每种策略的引用。 上下文并不执行任务, 而是将工作委派给已连接的策略对象。
上下文不负责选择符合任务需要的算法——客户端会将所需策略传递给上下文。 实际上, 上下文并不十分了解策略, 它会通过同样的通用接口与所有策略进行交互, 而该接口只需暴露一个方法来触发所选策略中封装的算法即可。
因此, 上下文可独立于具体策略。 这样你就可在不修改上下文代码或其他策略的情况下添加新算法或修改已有算法了。
策略模式结构
上下文 (Context) 维护指向具体策略的引用, 且仅通过策略接口与该对象进行交流。
策略 (Strategy) 接口是所有具体策略的通用接口, 它声明了一个上下文用于执行策略的方法。
具体策略 (Concrete Strategies) 实现了上下文所用算法的各种不同变体。
当上下文需要运行算法时, 它会在其已连接的策略对象上调用执行方法。 上下文不清楚其所涉及的策略类型与算法的执行方式。
客户端 (Client) 会创建一个特定策略对象并将其传递给上下文。 上下文则会提供一个设置器以便客户端在运行时替换相关联的策略。
实现方式
从上下文类中找出修改频率较高的算法 (也可能是用于在运行时选择某个算法变体的复杂条件运算符)。
声明该算法所有变体的通用策略接口。
将算法逐一抽取到各自的类中, 它们都必须实现策略接口。
在上下文类中添加一个成员变量用于保存对于策略对象的引用。 然后提供设置器以修改该成员变量。 上下文仅可通过策略接口同策略对象进行交互, 如有需要还可定义一个接口来让策略访问其数据。
客户端必须将上下文类与相应策略进行关联, 使上下文可以预期的方式完成其主要工作。
策略模式优缺点
优点:
你可以在运行时切换对象内的算法。
你可以将算法的实现和使用算法的代码隔离开来。
你可以使用组合来代替继承。
开闭原则。 你无需对上下文进行修改就能够引入新的策略。
缺点:
如果你的算法极少发生改变, 那么没有任何理由引入新的类和接口。 使用该模式只会让程序过于复杂。
客户端必须知晓策略间的不同——它需要选择合适的策略。
许多现代编程语言支持函数类型功能, 允许你在一组匿名函数中实现不同版本的算法。 这样, 你使用这些函数的方式就和使用策略对象时完全相同, 无需借助额外的类和接口来保持代码简洁。
Java示例代码:
public class StrategyPattern {
public static void main(String[] args) {
Strategy add = new AddStrategy();
Strategy subtraction = new SubtractionStrategy();
Strategy multiply = new MultiplyStrategy();
OperationContext context = new OperationContext(add);
context.Operation(2022, 528);
context = new OperationContext(subtraction);
context.Operation(2022, 528);
context = new OperationContext(multiply);
context.Operation(2022, 528);
}
}
class OperationContext {
private Strategy strategy;
public OperationContext(Strategy strategy) {
this.strategy = strategy;
}
public void Operation(int a, int b) {
strategy.TwoNumberOperation(a, b);
}
}
interface Strategy {
public void TwoNumberOperation(int a, int b);
}
class AddStrategy implements Strategy {
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a + b);
}
}
class SubtractionStrategy implements Strategy {
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a - b);
}
}
class MultiplyStrategy implements Strategy {
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a * b);
}
}