策略模式
基本介绍
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);}
工厂
@Componentpublic 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);}@Overridepublic 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)需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞
