设计模式-策略模式

参考:https://www.jianshu.com/p/3bcf55cf83d3

策略模式也叫政策模式。指的是对象具备某种行为,但是在不同的场景中,该行为有不同的实现。

策略模式使用的就是面向对象的继承和多态的机制,从而实现同一行为在不同场景下具备不同的实现。

策略模式本质:分离算法、选择实现。

策略模式主要解决在有很多种算法相似的情况下,比如if…else或者switch…case比较多,使系统看起来比较复杂臃肿,就可以考虑通过策略模式才进行优化。遵守开闭原则,对修改是封闭的,扩展是开放的。从而使系统易于扩展和维护。

2222997-69678af9fd117ec3.webp

从UML类图中,可以看出它包含三种角色:

  • 上下文角色(Context):用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略,算法的直接访问,封装可能存在的变化;

  • 抽象策略角色(Strategy):规定策略或算法的行为;

  • 具体策略角色(ConcreteStrategy):具体的策略或算法实现;

假如现在有一个需求,计算两个数,要求使用运算符+、-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Calculator {
private static final String ADD = "+";
private static final String SUB = "-";

public static int calc(int a,int b,final String symbol){
int result = 0;
if (ADD.equals(symbol)){
result = a + b;
} else if (SUB.equals(symbol)) {
result = a - b;
}
return result;
}
}

这是直接通过判断符号来对这两个数进行运算,如果我又有新的需求,需要*和/,那么就要在calc方法内去新增if…else判断,代码会随着需求越来越臃肿,而且这样扩展性很低。

采用策略模式后,代码如下:

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
59
60
// 抽象策略接口:
public interface CalculatorStrategy {
int calc(int a,int b);
}

// 加法策略
public class AddCalculator implements CalculatorStrategy{
@Override
public int calc(int a, int b) {
return a+b;
}
}

// 减法策略
public class SubCalculator implements CalculatorStrategy{
@Override
public int calc(int a, int b) {
return a-b;
}
}

// 乘法策略
public class MultiCalculator implements CalculatorStrategy{
@Override
public int calc(int a, int b) {
return a*b;
}
}

// 除法策略
public class DivideCalculator implements CalculatorStrategy{
@Override
public int calc(int a, int b) {
return a/b;
}
}

// 上下文
public class Context{
private CalculatorStrategy calculatorStrategy;

public Context(){}
public Context(CalculatorStrategy calculatorStrategy){
this.calculatorStrategy = calculatorStrategy;
}

public int calc(int a,int b){
return this.calculatorStrategy.calc(a,b);
}
}

// 客户端
public class Main {
public static void main(String[] args) {
CalculatorStrategy calculatorStrategy = new MultiCalculator();
Context context = new Context(calculatorStrategy);
int result = context.calc(10, 5);
System.out.println(result);
}
}

这样我们就可以消除对运算符判断的冗余,取而代之的是客户端来决定使用哪种策略,然后交给上下文去获取结果,后续如果有新的运算只需扩展相应的策略类而已。

注意:策略模式 中的上下文环境(Context),其职责本来是隔离客户端与策略类的耦合,让客户端完全与上下文环境沟通,无需关系具体策略。但是从上面的代码中我们可以看到,客户端内部直接自己指定要哪种策略(CalculatorStrategy calculatorStrategy= new MultiCalculator()),客户端与具体策略类耦合了,而上下文环境在这里其的作用只是负责调度执行,获取结果,并没有完全起到隔离客户端与策略类的作用。一般可以通过简单工厂模式将具体策略的创建与客户端进行隔离,或者是通过 策略枚举 将上下文环境与具体策略类融合在一起,简化代码。当具体策略相对稳定时,推荐使用 **策略枚举 **简化代码,具体代码如下:

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
class EnumClient {
public static void main(String[] args) {
int result = Calculator.ADD.calc(1, 2);
System.out.println(result);
// System.out.println(Calculator.ADD.getSymbol());
}

static enum Calculator {
// 加法运算
ADD("+") {
@Override
public int calc(int a, int b) {
return a + b;
}
},
SUB("-") {
@Override
public int calc(int a, int b) {
return a - b;
}
};
private String symbol;

private Calculator(String symbol) {
this.symbol = symbol;
}

public String getSymbol() {
return this.symbol;
}

public abstract int calc(int a, int b);
}
}