一、策略模式介绍

策略模式是一种行为模式,也是替代大量 if-else 的利器。策略模式所能解决的一般是具有同类可替代的行为逻辑算法场景。在多种相似算法存在时,使用条件语句(如 if...else)导致的复杂性和难以维护的问题。

策略模式强调的是灵活切换,比如一个类的多个方法有着类似的行为接口,可以将它们抽离出来作为一系列策略类,在运行时灵活对接,变更其算法策略,以适应不同的场景。

一个设计优秀的系统,绝不能来回更改底层代码,反复的代码修改会让系统维护变成灾难,最终大量的方法被堆积在同一个类中,臃肿不堪。而是要站在高层抽象的角度构筑一套相对固化的模式,并能使新加入的代码以实现类的方式接入系统,让系统功能得到无限的算法扩展,以适应用户需求的多样性。

策略模式包含以下几个核心角色:

  • 上下文(Context):维护一个对策略对象的引用,负责将客户端请求委派给具体的策略对象执行。环境类可以通过依赖注入、简单工厂等方式来获取具体策略对象。
  • 抽象策略(Abstract Strategy):定义了策略对象的公共接口或抽象类,规定了具体策略类必须实现的方法。
  • 具体策略(Concrete Strategy):实现了抽象策略定义的接口或抽象类,包含了具体的算法实现。

策略模式建议找出负责用许多不同方式完成特定任务的类,然后将其中的算法抽取到一组被称为策略的独立类中。名为上下文的原始类必须包含一个成员变量来存储对于每种策略的引用。上下文并不执行任务,而是将工作委派给已连接的策略对象。上下文不负责选择符合任务需要的算法——客户端会将所需策略传递给上下文。实际上,上下文并不十分了解策略,它会通过同样的通用接口与所有策略进行交互,而该接口只需暴露一个方法来触发所选策略中封装的算法即可。因此,上下文可独立于具体策略。这样你就可在不修改上下文代码或其他策略的情况下添加新算法或修改已有算法了。

适合场景

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

优点

  • 可以在运行时切换对象内的算法。
  • 可以将算法的实现和使用算法的代码隔离开来。
  • 可以使用组合来代替继承。
  • 开闭原则。无需对上下文进行修改就能够引入新的策略。

缺点

  • 如果算法极少发生改变,那么没有任何理由引入新的类和接口。使用该模式只会让程序过于复杂。
  • 客户端必须知晓策略间的不同——它需要选择合适的策略。
  • 许多现代编程语言支持函数类型功能,允许一组匿名函数中实现不同版本的算法。这样,你使用这些函数的方式就和使用策略对象时完全相同,无需借助额外的类和接口来保持代码简洁。

二、策略模式示例

1、代码示例

策略接口:

1
2
3
4
5
6
7
8
9
10
11
interface Strategy {

/**
*
* @param a 操作数
* @param b 被操作数
* @return 计算结果
*/
int calculate(int a, int b);

}

策略实现类:

1
2
3
4
5
6
7
8
class Add implements Strategy {  

@Override
public int calculate(int a, int b) {
return a + b;
}

}
1
2
3
4
5
6
7
class Subtraction implements Strategy{  

@Override
public int calculate(int a, int b) {
return a - b;
}
}

上下文类:

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
class Calculator {  

private Strategy strategy;

public Calculator() {
}

public Calculator(Strategy strategy) {
this.strategy = strategy;
}

public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}

public int getResult(int a, int b) {
return strategy.calculate(a, b);
}

public static void main(String[] args) {
Calculator calculator = new Calculator();
calculator.setStrategy(new Add());
System.out.println(calculator.getResult(1, 2));

calculator.setStrategy(new Subtraction());
System.out.println(calculator.getResult(1, 2));
}

}

2、Spring Web 中的 HandlerMethodArgumentResolver

1
2
3
4
5
6
7
8
9
public interface HandlerMethodArgumentResolver {

boolean supportsParameter(MethodParameter parameter);

@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

用于在给定请求的上下文中将方法参数解析为参数值的策略接口。

接口中有两个方法:

  • supportsParameter 方法,用来判断此解析器是否支持给定的方法参数。
  • resolveArgument 方法,从给定请求中将方法参数解析为参数值。ModelAndViewContainer 提供了对请求模型的访问。WebDataBinderFactory 在需要进行数据绑定和类型转换时提供了一种创建 WebDataBinder 实例的方式。

该策略接口有许多种具体实现,比如有实现类用来处理 @RequestParam@RequestHeader@PathVariable@RequestBody 等注解方法参数。

比如:

  • RequestParamMethodArgumentResolver
  • PathVariableMethodArgumentResolver
  • RequestResponseBodyMethodProcessor

相关链接

策略设计模式

OB tags

#设计模式