策略,可以简单理解为解决某个问题的具体方法。
当一个问题的解决方法有多种,并且需要根据具体场景灵活替换时,我们可以使用策略模式。
策略模式的概念不多说,借网上的图来描述一下:

策略的实现依赖于具体的业务场景,这个没什么好说的;但是策略的管理是一种比较通用的方式,也是本文探讨的主要内容。
以上图为例,Context维护着策略的实现,executeState接口屏蔽了这种实现。除了提供获取策略的途径外,Context还需要做下面两件事:
1. 策略收集。对于Context来说,策略交给他管理后,他需要知道一共有哪些策略。
2. 策略配置。即当前生效的策略时哪一个。(或者给executeStrategy加个参数policyName,客户端直接传进来,可以参考下面的案例)
在平时的开发过程中,如果我们使用Spring或者相关的容器框架,可以考虑把所有的策略放到容器中。所以策略管理,可以简单地实现为:将所有实现Stategy接口的类从容器中取出来,放入Context中。
示例代码如下:
策略接口
- public interface Strategy {
-
- int doOperation();
- }
实现类
- @Component
- public class StrategyA implements Strategy{
- @Override
- public int doOperation() {
- return 1;
- }
- }
- @Component
- public class StrategyB implements Strategy{
- @Override
- public int doOperation() {
- return 2;
- }
- }
Context
- @Component
- public class StrategyContext {
-
- @Autowired
- private ApplicationContext applicationContext;
-
- private Map<String, Strategy> strategyMap = new HashMap<>();
-
- @PostConstruct
- public void init() {
- Map<String, Strategy> impls = applicationContext.getBeansOfType(Strategy.class);
- for (String key : impls.keySet()) {
- strategyMap.put(key, impls.get(key));
- }
- }
-
- public Strategy get(String policyName) {
- return strategyMap.get(policyName);
- }
- }
上述方法的问题点在于,针对每一组策略的Context,都需通过硬编码的方式来管理。

什么意思呢?一个项目中可能不止有一个策略。比如
Strategy1策略有对应的实现StrategyA、StrategyB、StrategyC;
Strategy2有对应的实现StrategyD、StrategyE、StrategyF。
当需要做成策略的功能点很多时,这个时候就需要写很多个init方法,维护很多个strategyMapt,出现了很多的冗余代码。
我们以DispatchServlet为例

DispatcherServlet初始化的时候,会调用initStrategies方法

以HandlerMapping为例,通过getDefaultStrategies获取到HandlerMapping所有的实例

getDefaultStrategies具体实现

配置文件的全貌

配置文件中的每一个键值对代表一个策略组。key是策略接口,value是策略实现的集合。
后面逻辑就很清晰了,通过key(策略)获取到values(策略实现)后,创建对应的策略实现。

上述案例中的key,value是对应策略的全路径。但对于我们开发者来说,在实际项目中不必照搬Spring的所有实现。
比如key可以设置为策略组的名称,value设置为策略实现的名称。获取策略的时候也不用去创建策略,可以兼容之前的做法,直接通过名称去Spring容器中取。
这种方式确实可以减少一些重复代码,但付出的代价是,每次获取策略的时候,需要额外提供一个策略组的名称。
总体看来,只能说是有利有弊吧。但如果开发构建大型系统,策略组特别多的话,这种方式还是很有必要的。