设计模式之策略模式

设计模式之策略模式

意图

策略模式(Strategy) 是一种行为设计模式, 它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。

适用场景

  • 当你想使用对象中各种不同的算法变体, 并希望能在运行时切换算法时, 可使用策略模式。
  • 当你有许多仅在执行某些行为时略有不同的相似类时, 可使用策略模式。
  • 如果算法在上下文的逻辑中不是特别重要, 使用该模式能将类的业务逻辑与其算法实现细节隔离开来。
  • 当类中使用了复杂条件运算符以在同一算法的不同变体中切换时, 可使用该模式。

结构

结构说明

img

  1. 上下文 (Context) 维护指向具体策略的引用, 且仅通过策略接口与该对象进行交流。
  2. 策略 (Strategy) 接口是所有具体策略的通用接口, 它声明了一个上下文用于执行策略的方法。
  3. 具体策略 (Concrete Strategies) 实现了上下文所用算法的各种不同变体。
  4. 当上下文需要运行算法时, 它会在其已连接的策略对象上调用执行方法。 上下文不清楚其所涉及的策略类型与算法的执行方式。
  5. 客户端 (Client) 会创建一个特定策略对象并将其传递给上下文。 上下文则会提供一个设置器以便客户端在运行时替换相关联的策略。

结构代码范式

Strategy : 定义所有算法的公共接口(AlgorithmInterface)。Context 使用这个接口去调用 ConcreteStrategy 定义的具体算法。

1
2
3
abstract class Strategy {
public abstract void AlgorithmInterface();
}

ConcreteStrategy : 实现 Strategy 中的算法接口(AlgorithmInterface)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ConcreteStrategyA extends Strategy {
@Override
public void AlgorithmInterface() {
System.out.println("算法A");
}
}

class ConcreteStrategyB extends Strategy {
@Override
public void AlgorithmInterface() {
System.out.println("算法B");
}
}

class ConcreteStrategyC extends Strategy {
@Override
public void AlgorithmInterface() {
System.out.println("算法C");
}
}

Context : 用一个 ConcreteStrategy 来配置。维护一个对 Strategy 对象的引用。

1
2
3
4
5
6
7
8
9
10
class Context {
Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}

public void ContextInterface() {
strategy.AlgorithmInterface();
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
public class StrategyPattern {
public static void main(String[] args) {
Context context1 = new Context(new ConcreteStrategyA());
context1.ContextInterface();

Context context2 = new Context(new ConcreteStrategyB());
context2.ContextInterface();

Context context3 = new Context(new ConcreteStrategyC());
context3.ContextInterface();
}
}

输出

1
2
3
算法A
算法B
算法C

伪代码

在本例中, 上下文使用了多个策略来执行不同的计算操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 策略接口声明了某个算法各个不同版本间所共有的操作。上下文会使用该接口来
// 调用有具体策略定义的算法。
interface Strategy is
method execute(a, b)

// 具体策略会在遵循策略基础接口的情况下实现算法。该接口实现了它们在上下文
// 中的互换性。
class ConcreteStrategyAdd implements Strategy is
method execute(a, b) is
return a + b

class ConcreteStrategySubtract implements Strategy is
method execute(a, b) is
return a - b

class ConcreteStrategyMultiply implements Strategy is
method execute(a, b) is
return a * b

// 上下文定义了客户端关注的接口。
class Context is
// 上下文会维护指向某个策略对象的引用。上下文不知晓策略的具体类。上下
// 文必须通过策略接口来与所有策略进行交互。
private strategy: Strategy

// 上下文通常会通过构造函数来接收策略对象,同时还提供设置器以便在运行
// 时切换策略。
method setStrategy(Strategy strategy) is
this.strategy = strategy

// 上下文会将一些工作委派给策略对象,而不是自行实现不同版本的算法。
method executeStrategy(int a, int b) is
return strategy.execute(a, b)


// 客户端代码会选择具体策略并将其传递给上下文。客户端必须知晓策略之间的差
// 异,才能做出正确的选择。
class ExampleApplication is
method main() is

创建上下文对象。

读取第一个数。
读取最后一个数。
从用户输入中读取期望进行的行为。

if (action == addition) then
context.setStrategy(new ConcreteStrategyAdd())

if (action == subtraction) then
context.setStrategy(new ConcreteStrategySubtract())

if (action == multiplication) then
context.setStrategy(new ConcreteStrategyMultiply())

result = context.executeStrategy(First number, Second number)

打印结果。

案例

使用示例: 策略模式在 Java 代码中很常见。 它经常在各种框架中使用, 能在不扩展类的情况下向用户提供改变其行为的方式。

Java 8 开始支持 lambda 方法, 它可作为一种替代策略模式的简单方式。

这里有一些核心 Java 程序库中策略模式的示例:

识别方法: 策略模式可以通过允许嵌套对象完成实际工作的方法以及允许将该对象替换为不同对象的设置器来识别。

与其他模式的关系

  • 桥接模式状态模式策略模式 (在某种程度上包括适配器模式) 模式的接口非常相似。 实际上, 它们都基于组合模式——即将工作委派给其他对象, 不过也各自解决了不同的问题。 模式并不只是以特定方式组织代码的配方, 你还可以使用它们来和其他开发者讨论模式所解决的问题。
  • 命令模式策略看上去很像, 因为两者都能通过某些行为来参数化对象。 但是, 它们的意图有非常大的不同。
    • 你可以使用命令来将任何操作转换为对象。 操作的参数将成为对象的成员变量。 你可以通过转换来延迟操作的执行、 将操作放入队列、 保存历史命令或者向远程服务发送命令等。
    • 另一方面, 策略通常可用于描述完成某件事的不同方式, 让你能够在同一个上下文类中切换算法。
  • 装饰模式可让你更改对象的外表, 策略则让你能够改变其本质。
  • 模板方法模式基于继承机制: 它允许你通过扩展子类中的部分内容来改变部分算法。 策略基于组合机制: 你可以通过对相应行为提供不同的策略来改变对象的部分行为。 模板方法在类层次上运作, 因此它是静态的。 策略在对象层次上运作, 因此允许在运行时切换行为。
  • 状态可被视为策略的扩展。 两者都基于组合机制: 它们都通过将部分工作委派给 “帮手” 对象来改变其在不同情景下的行为。 策略使得这些对象相互之间完全独立, 它们不知道其他对象的存在。 但状态模式没有限制具体状态之间的依赖, 且允许它们自行改变在不同情景下的状态。

参考资料