策略模式的基本结构
策略,可以简单理解为解决某个问题的具体方法。
当一个问题的解决方法有多种,并且需要根据具体场景灵活替换时,我们可以使用策略模式。
策略模式的概念不多说,借网上的图来描述一下:
策略的实现依赖于具体的业务场景,这个没什么好说的;但是策略的管理是一种比较通用的方式,也是本文探讨的主要内容。
以上图为例,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,出现了很多的冗余代码。
Spring是如何解决这个问题的
我们以DispatchServlet为例
DispatcherServlet初始化的时候,会调用initStrategies方法
以HandlerMapping为例,通过getDefaultStrategies获取到HandlerMapping所有的实例
getDefaultStrategies具体实现
配置文件的全貌
配置文件中的每一个键值对代表一个策略组。key是策略接口,value是策略实现的集合。
后面逻辑就很清晰了,通过key(策略)获取到values(策略实现)后,创建对应的策略实现。
如何实际运用
上述案例中的key,value是对应策略的全路径。但对于我们开发者来说,在实际项目中不必照搬Spring的所有实现。
比如key可以设置为策略组的名称,value设置为策略实现的名称。获取策略的时候也不用去创建策略,可以兼容之前的做法,直接通过名称去Spring容器中取。
这种方式确实可以减少一些重复代码,但付出的代价是,每次获取策略的时候,需要额外提供一个策略组的名称。
总体看来,只能说是有利有弊吧。但如果开发构建大型系统,策略组特别多的话,这种方式还是很有必要的。