策略模式
基本介绍
1)策略模式(StrategyPattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
2)这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。
说明:从上图可以看到,客户context有成员变量strategy或者其他的策略接口,至于需要使用到哪个策略,我们可以在构造器中指定
策略模式优化IF else
类图
使用策略模式+工厂+模板方法 解决多分类查询
- 浏览器调用Controller, Controller调用Service, Service调用查询工厂
- 在WarningQueryFactory中维护查询Key和具体查询的的关系, 并实现ApplicationContextAware接口获取到IOC, 然后通过IOC获取抽象查询类调用(AbstractWarningQuery, 并调用抽象查询类的模板方法
- 在WaringQuery接口中定义统一查询方法
- 使用AbstractWaringQuery对其进行实现, 并在其中定义模板方法, 并且在模板方法中调用抽象接口
- 具体查询实现类继承抽象查询类, 并实现WarningQuery接口中的查询方法, 同时注册到IOC中, 可以让工厂从IOC中获取到
这是一个标准的策略+模板的实现. 本来我想在其中加入状态模式, 用于控制是根据一些参数, 来决定查询DB还是查询缓存, 但是后来应为一些场景是缓存实现不了的, 只能查DB了, 但是一些公用数据还是查询缓存的, 后续如果需要扩展其他查询, 只需要在工厂层中维护映射关系, 并添加新的实现类继承抽象查询类(AbstractWariningQuery), 并实现WarningQuery接口的查询方法即可
代码实现
Controller层
@GetMapping("/queryWarningByKeyPage")
public Object queryWarningByKey(@Valid WarningQueryParam warningQueryParam) {
return warningService.queryWarningByKey(warningQueryParam);
}
Service层
/**
* 根据配置决定查询数据库还是缓存默认为数据库,缓存还有问题(2022/5/12 已修复)
*
* @param warningQueryParam 预警查询条件
* @return 数据
*/
public Object queryWarningByKey(WarningQueryParam warningQueryParam) {
String key = warningQueryParam.getKey();
String deptName = warningQueryParam.getDeptName();
Long pageNum = warningQueryParam.getPageNum();
Long pageSize = warningQueryParam.getPageSize();
return warningQueryFactory.queryDataByKey(key, deptName, pageNum, pageSize);
}
工厂
@Component
public class WarningQueryFactory implements ApplicationContextAware {
public static final ConcurrentHashMap<String, String> context = new ConcurrentHashMap<>();
private ApplicationContext applicationContext;
static {
// context.put("021","opwWarningQuery");
context.put("022","opwWarningQuery");
String videoWarning = "videoWarningQuery";
context.put("011",videoWarning);
}
public Object queryDataByKey(String key,String deptName,Long pageNum, Long pageSize){
if("021".equals(key)){
throw new BusinessException("该类型接口暂未实现, 敬请期待!");
}
String s = context.get(key);
AbstractWaringQuery bean = applicationContext.getBean(s, AbstractWaringQuery.class);
return bean.queryWarningByKeyPageBase(key,deptName,pageNum,pageSize);
}
@Override
public void setApplicationContext(@Nonnull ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
高层查询接口
public interface WaringQuery {
Object queryWaringByKeyPage(String key,String deptName, Long pageNum, Long pageSize);
}
抽象实现者
public abstract class AbstractWaringQuery implements WaringQuery {
public Object queryWarningByKeyPageBase(String key,String deptName,Long pageNum, Long pageSize){
if(pageNum == null || pageNum < 1){
pageNum = 1L;
}
if(pageSize == null || pageSize < 1){
pageSize = 4L;
}
return queryWaringByKeyPage(key,deptName,pageNum,pageSize);
}
}
具体实现者OPW
@Component
public class OpwWarningQuery extends AbstractWaringQuery {
@Autowired
private RedisUtil redisUtil;
@Autowired
private DeptService deptService;
@Autowired
private PressureService pressureService;
@Value("${query.warning.type}")
private String queryType;
@Autowired
private UserContextService userContextService;
@Autowired
private OnlineService onlineService;
@Override
public CommonPage<List<PressureVo>> queryWaringByKeyPage(String key, String deptName, Long pageNum, Long pageSize) {
// 暂时不支持Redis了
// if (QueryType.REDIS.equals(queryType)) {
// return queryDataByRedis(key, pageNum, pageSize);
// }
return queryDateByDb(key,deptName, pageNum, pageSize);
}
private CommonPage<List<PressureVo>> queryDateByDb(String key,String deptName, Long pageNum, Long pageSize) {
// 业务逻辑
}
private CommonPage<List<PressureVo>> queryDataByRedis(String key, Long pageNum, Long pageSize) {
// 业务逻辑
}
private List<PressureVo> addIsOnline(List<Pressure> records, List<String> deptCodes) {
// 业务逻辑
}
}
策略模式的注意事项和细节
1)策略模式的关键是:分析项目中变化部分与不变部分
2)策略模式的核心思想是:多用组合/聚合少用继承;用行为类组合,而不是行为的继承。更有弹性
3)体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(if..elseif..else)
4)提供了可以替换继承关系的办法:策略模式将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展
5)需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞