策略模式

策略模式

一、什么是策略模式?

策略模式(Strategy Design Pattern):定义一系列算法,并将算法封装起来,使得它们可以相互替换。策略模式可以使得算法独立于客户端(即使用算法的地方)。

英语原文:

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

策略模式有几个特点:

  • 存在多种算法
  • 算法之间可以相互替换,说明它们返回结果的影响是一样的,只是算法内部实现不同
  • 策略独立于客户端,单独实现

比如说,排序功能,排序的结果可能都是一样的,但是可以使用不同的算法,如选择排序、冒泡排序、快速排序等。

二、为什么要用策略模式?

2.1 好处在于:策略与客户端解耦

策略独立封装,在客户端那边是感知不到细节的,很方便扩展新的实现。

2.2 典型应用场景:在运行时动态选择策略

比如说,程序中设置一个类型变量,指定某种策略,然后在运行时改变类型值,就能动态改变策略。

例如,排序策略,数据很少时,直接用冒泡排序,数据很多时,可以用归并排序。

2.3 利用策略模式来移除if-else语句

策略独立封装后,就可以采用查表法去获取指定的策略,无需使用if-else语句。

1
Strategy strategy = StrategyFactory.get(strategyType);

当然,实际上if-else语句并没有移除,只是从客户端转移到了策略获取的地方。

但是这样做就减少了客户端的工作量,避免了很多出错的情况。

三、怎么用策略模式?

使用策略模式,重点在于策略之间的可替换性。

策略模式中的角色包括:

  • 策略(Strategy):定义了策略所必须的接口。
  • 具体策略(ConcreteStrategy):实现了策略接口的具体策略(战略、方法和算法)
  • 上下文(Context):使用策略的地方,即调用Strategy接口的地方

策略模式的结构:

策略模式结构

示例程序结构:

策略模式结构

策略(Strategy):

1
2
3
4
public interface Strategy {
Hand nextHand();
void study(boolean win);
}

具体策略(ConcreteStrategy):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class WinningStrategy implements Strategy {

private Random random;
private boolean won = false;
private Hand preHand;

public WinningStrategy(int seed) {
random = new Random(seed);
}

@Override
public Hand nextHand() {
if (!won) {
preHand = Hand.getHand(random.nextInt(3));
}
return preHand;
}

@Override
public void study(boolean win) {
won = win;
}
}
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
public class ProbStrategy implements Strategy {

private Random random;
private int preHandValue = 0;
private int currentHandValue = 0;
private int[][] history = {
{1, 1, 1},
{1, 1, 1},
{1, 1, 1}
};

public ProbStrategy(int seed) {
random = new Random(seed);
}

@Override
public Hand nextHand() {
int bet = random.nextInt(getSum(currentHandValue));
int handValue = 0;
if (bet < history[currentHandValue][0]) {
handValue = 0;
} else if (bet < history[currentHandValue][0] + history[currentHandValue][1]) {
handValue = 1;
} else {
handValue = 2;
}
preHandValue = currentHandValue;
currentHandValue = handValue;
return Hand.getHand(handValue);
}

@Override
public void study(boolean win) {
if (win) {
history[preHandValue][currentHandValue]++;
} else {
history[preHandValue][(currentHandValue + 1) % 3]++;
history[preHandValue][(currentHandValue + 2) % 3]++;
}
}

private int getSum(int hv) {
int sum = 0;
for (int i = 0; i < 3; i++) {
sum += history[hv][i];
}
return sum;
}
}

上下文(Context):

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
public class Player {

private String name;
private Strategy strategy;
private int winCount;
private int loseCount;
private int gameCount;

public Player(String name, Strategy strategy) {
this.name = name;
this.strategy = strategy;
}

public Hand nextHand() {
return strategy.nextHand();
}

public void win() {
strategy.study(true);
winCount++;
gameCount++;
}

public void lose() {
strategy.study(false);
loseCount++;
gameCount++;
}

public void even() {
gameCount++;
}

}

四、策略模式有什么优缺点?

优点:

  • 策略与客户端解耦,可扩展性好
  • 策略独立扩展,符合单一职责原则
  • 策略之间可相互替代,符合里斯替换原则
  • 在一定程度上,策略模式可以减少客户端的if-else语句,避免代码错误

缺点:

  • 策略类可能会很多,后期会比较难维护
  • 客户端需要了解各种策略的区别(但不需要了解实现细节),才能选择合适的策略
作者

jiaduo

发布于

2022-01-19

更新于

2023-04-03

许可协议