06-10-设计模式 策略模式
策略模式
基本介绍
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)需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞