1. 策略模式简介

策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变更和替换独立于使用算法的客户。

策略模式中体现了如下几种设计原则:

  1. 针对接口编程,而不是具体的实现类(定义了一个策略组接口)
  2. 使用组合/聚合的方式代替继承(StrategyContext类与策略组容器就是组合关系)
  3. 把变化的代码从不变的代码中分离出来

原理类图

Context:负责和具体的策略类交互;这样的话具体的算法和客户端调用分离了,使得算法可以独立于客户端独立的变化;

Strategy,B:策略类的抽象构建角色,定义规范或者策略类共有的部分(java8开始,接口可以定义default修饰,以及static修饰的方法);

ConcreteStrategyA,B,C,D:具体的策略实现;

2. 实例

本实例使用SSM框架实现。

2.1 Controller层

类中@AutoWriter注解标注的类StrategyContext strategyContext;就是上面类图中的Context;

接口compMonthlyData接收的参数之一String queryWay;来决定用户使用哪种算法;

package com.icex.bus.sys.controller;import com.icex.frame.utils.DateUtil;
import com.icex.frame.utils.ReturnUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 投诉月度统计类* @author Jjcc* @version 1.0.0* @description* @className SysComplaintMonthlyController.java* @createTime 2019年09月04日 09:46:00*/
@Controller
@RequestMapping("/sys/comp/monthly")
public class SysComplaintMonthlyController {@AutowiredStrategyContext strategyContext;/*** 投诉月度统计页面* @title compMonthlyPage* @description* @author Jjcc* @return java.lang.String* @createTime 2019/9/5 10:05* @throws*/@RequestMapping("compMonthlyPage")public String compMonthlyPage() {return "topJUI/sys/compDataStat/compMonthly/compMonthly_list";}/*** 投诉月度统计获取数据* @title compMonthlyData* @description* @author Jjcc* @param queryYear 年份* @param queryWay 根据单位,供电所...来统计数据* @return java.lang.Object* @createTime 2019/9/9 11:20* @throws*/@RequestMapping("compMonthlyData.json")@ResponseBodypublic Object compMonthlyData(Integer queryYear, @RequestParam(required = false, defaultValue = "zrdw") String queryWay) {Map<String, Object> map = new HashMap<>(8);try {if (null == queryYear) {map = ReturnUtil.buildRetFailMap("查询年份不能为空", null);return map;}if (StringUtils.isBlank(queryWay)) {map = ReturnUtil.buildRetFailMap("queryWay不能为空", null);return map;}int beginYear = queryYear - 1;StringBuilder beginTime = new StringBuilder();StringBuilder endTime = new StringBuilder();beginTime.append(beginYear);beginTime.append("-12-26 00:00:00");endTime.append(queryYear);endTime.append("-12-26 00:00:00");//查询条件Map<String, Object> condition = new HashMap<>(8);condition.put("beginTime", DateUtil.getDateFromString(beginTime.toString(), "yyyy-MM-dd 00:00:00"));condition.put("endTime", DateUtil.getDateFromString(endTime.toString(), "yyyy-MM-dd 00:00:00"));List<HashMap<String, Object>> list = strategyContext.getCompMonthlyData(queryWay, condition);map.put("list", list);map.put("startTime", beginTime.toString());map.put("endTime",endTime.toString());map = ReturnUtil.buildRetSuccMap(map);} catch (Exception e) {map = ReturnUtil.buildRetErrMap(null);e.printStackTrace();}return map;}
}

2.2 StrategyContex

使用@Component将该类注册至IOC容器中;

关于@AutoWriter标注在Map容器中具体可以百度,这里不过多细说;

package com.icex.bus.sys.controller;import com.icex.bus.enums.CompMonthlyTypeEnum;
import com.icex.bus.sys.service.ICompMonthlyStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 策略管理器* @author Jjcc* @version 1.0.0* @description* @className StrategyContext.java* @createTime 2019年09月09日 14:23:00*/
@Component
public class StrategyContext {/*** 注入ICompMonthlyStrategy子类的service,key为子类service的key*/@Autowiredprivate Map<String, ICompMonthlyStrategy> compMonthlyStrategy;public List<HashMap<String, Object>> getCompMonthlyData(String queryWay, Map<String, Object> condition){//调用枚举类中的getEnumByType方法通过参数queryWay获取对应的算法类信息CompMonthlyTypeEnum enumByType = CompMonthlyTypeEnum.getEnumByType(queryWay);//Spring容器初始化后,所有符合要求的Bean都会存入Map容器中,map的key即bean的nameICompMonthlyStrategy iCompMonthlyStrategy = compMonthlyStrategy.get(enumByType.getService());//调用算法族中共有的封装装算法的接口List<HashMap<String, Object>> compMonthlyData = iCompMonthlyStrategy.getCompMonthlyData(condition);return compMonthlyData;}
}

2.3 CompMonthlyTypeEnum枚举类

枚举类中,定义了多个初始化枚举的方法,当你新增了策略类时,只需在枚举类中新增初始化方法即可;

这里使用枚举当工厂的优势是,枚举类是一个单例,并且天然的防止反射以及反序列化创建对象;

package com.icex.bus.enums;/*** compMonthly算法族枚举类,这里相当于工厂;* @author Jjcc* @version 1.0.0* @description* @className compMonthly.java* @createTime 2019年09月09日 11:08:00*/
public enum CompMonthlyTypeEnum {/*** 根据责任单位计算投诉月度统计* @title* @description* @author Jjcc* @return* @createTime 2019/9/9 11:38* @throws*/ZRDW("zrdw", "根据责任单位计算投诉月度统计", "compMonthlyUnitImpl"),/*** 根据供电所计算投诉月度统计* @title* @description* @author Jjcc* @return* @createTime 2019/9/9 11:38* @throws*/GDS("gds", "根据供电所计算投诉月度统计", "compMonthlyPsStationImpl"),/*** 根据各地市计算投诉月度统计* @title* @description* @author Jjcc* @return* @createTime 2019/9/10 14:18* @throws*/EACHCities("eachCities", "根据各地市计算投诉月度统计", "compMonthlyEachCitiesImpl"),/*** 根据考核班组计算投诉月度统计* @title* @description* @author Jjcc* @return* @createTime 2019/9/10 14:18* @throws*/KHbz("khbz", "根据考核班组计算投诉月度统计", "compMonthlyAssessTeamImpl"),/*** 根据施工队计算投诉月度统计* @title* @description* @author Jjcc* @return* @createTime 2019/9/11 16:04* @throws*/SGD("sgd", "根据施工队计算投诉月度统计", "compMonthlyConstructionTeamImpl"),/*** 根据线路计算投诉月度统计* @title* @description* @author Jjcc* @return* @createTime 2019/9/11 16:15* @throws*/XL("xl", "根据线路名称计算投诉月度统计", "compMonthlyCircuitNameImpl");/*** 类型*/private String queryWay;/*** 描述*/private String description;/*** service的BeanName*/private String service;public String getQueryWay() {return queryWay;}public String getDescription() {return description;}public String getService() {return service;}/*** @title CompMonthlyTypeEnum* @description * @author Jjcc * @param queryWay 客户调用何种算法的参数* @param description 描述* @param service 算法Bean的name* @return * @createTime 2019/9/19 15:24* @throws */CompMonthlyTypeEnum(String queryWay, String description, String service) {this.queryWay = queryWay;this.description = description;this.service = service;}/*** 根据参数queryWay获取需要调用的算法类信息* @title getEnumByType* @description* @author Jjcc* @param queryWay* @return com.icex.bus.enums.CompMonthlyTypeEnum* @createTime 2019/9/19 15:16* @throws*/public static CompMonthlyTypeEnum getEnumByType(String queryWay) {for (CompMonthlyTypeEnum value : CompMonthlyTypeEnum.values()) {if (value.getQueryWay().equals(queryWay)) {return value;}}throw new RuntimeException("通过key获取枚举类异常");}}

2.4 算法族抽象构建类

请忽略本人写的逻辑代码,初始版,有待优化;

package com.icex.bus.sys.service;import java.text.NumberFormat;
import java.util.*;
import java.util.stream.Stream;/*** 算法族抽象构建类* @author Jjcc* @version 1.0.0* @description* @className ICompMonthlyStrategy.java* @createTime 2019年09月09日 10:30:00*/
public interface ICompMonthlyStrategy {/*** 策略方法* @title getCompMonthlyData* @description* @author Jjcc* @param condition* @return java.util.HashMap<java.lang.String,java.lang.Object>* @createTime 2019/9/9 10:40* @throws*/List<HashMap<String, Object>> getCompMonthlyData(Map<String, Object> condition);/*** 算法通用代码;* 用于group by字段出现 “/” 的问题* @title groupFieldMultiOne* @description* @author Jjcc* @param list* @return void* @createTime 2019/9/9 17:33* @throws*/default void groupFieldMultiOne(List<HashMap<String, Object>> list){NumberFormat nf = NumberFormat.getNumberInstance();nf.setMaximumFractionDigits(2);list.stream().filter(p -> {String str = String.valueOf(p.get("group_field_two"));if (str.contains("/")) {return true;}return false;}).forEach(c -> {//责任单位字段含有2个或2个以上部门String dutyUnit = String.valueOf(c.get("group_field_two"));String[] dutyUnits = dutyUnit.split("/");for (int i = 0; i<dutyUnits.length; i++) {if("检修公司配电室".equals(dutyUnits[i])) {dutyUnits[i] = "配电检修公司";} else if("检修公司电缆室".equals(dutyUnits[i])) {dutyUnits[i] = "电缆检修公司";} else if("检修公司输电室".equals(dutyUnits[i])) {dutyUnits[i] = "输电检修公司";} else if("检修公司检修室".equals(dutyUnits[i]) || "检修公司运维室".equals(dutyUnits[i])) {dutyUnits[i] = "变电检修公司";}}Set<Map.Entry<String, Object>> set = new HashSet<>();c.entrySet().forEach(cMap -> {//将含有多个部门的不为空的数据存入Setif (!"zrdw".equals(String.valueOf(cMap.getKey())) && !"group_field_two".equals(String.valueOf(cMap.getKey())) && !"0".equals(String.valueOf(cMap.getValue()))) {System.out.println("mSet:" +cMap);set.add(cMap);}});Stream.of(dutyUnits).forEach(m -> {list.stream().filter(pTarget -> {String deptName = String.valueOf(pTarget.get("group_field_one"));return "null".equals(deptName) ? false : true ;}).forEach(listMap -> {String dutyUnitName = String.valueOf(listMap.get("group_field_one"));if (m.equals(dutyUnitName)) {set.forEach(mSet -> {double s1 = Double.parseDouble((listMap.get(mSet.getKey())).toString());double s2 = Double.parseDouble(mSet.getValue().toString()) / dutyUnits.length;listMap.put(mSet.getKey(), nf.format(s1 + s2));});}});});});//根据总计字段降序排序list.sort(Comparator.comparingDouble((t) -> -Double.parseDouble(t.get("counts").toString())));}/*** 算法通用代码-考核班组,施工队,线路;* 用于group by字段出现 “/” 的问题* @title groupFieldMultiOne* @description* @author Jjcc* @param list* @return void* @createTime 2019/9/9 17:33* @throws*/default void groupFieldMultiTwo(List<HashMap<String, Object>> list) {NumberFormat nf = NumberFormat.getNumberInstance();nf.setMaximumFractionDigits(2);List<HashMap<String, Object>> newAddList = new ArrayList<>();HashMap<String, Object> clone = (HashMap<String, Object>)list.get(0).clone();for (Map.Entry<String, Object> stringObjectEntry : clone.entrySet()) {stringObjectEntry.setValue("0");}list.stream().filter(p -> {String str = String.valueOf(p.get("group_field_two"));if (str.contains("/")) {return true;}return false;}).forEach(c -> {//字段含有2个或2个以上部门String dutyUnit = String.valueOf(c.get("group_field_two"));String[] dutyUnits = dutyUnit.split("/");Set<Map.Entry<String, Object>> set = new HashSet<>();c.entrySet().forEach(cMap -> {//将含有多个部门的不为空的数据存入Setif (!"group_field_two".equals(String.valueOf(cMap.getKey())) && !"0".equals(String.valueOf(cMap.getValue()))) {set.add(cMap);}});Stream.of(dutyUnits).forEach(m -> {//字段中多个部门中的某一个部门在list中没有找到的话,需要在list中新增boolean boole = list.stream().filter(pTarget -> {String deptName = String.valueOf(pTarget.get("group_field_one"));return !"null".equals(deptName) ? true : false;}).anyMatch(listMap -> {String dutyUnitName = String.valueOf(listMap.get("group_field_one"));if (m.equals(dutyUnitName)) {//多部门中的某一个部门存在于list集合中,进行相应的数据调整;set.forEach(mSet -> {double s1 = Double.parseDouble((listMap.get(mSet.getKey())).toString());double s2 = Double.parseDouble(mSet.getValue().toString()) / dutyUnits.length;listMap.put(mSet.getKey(), nf.format(s1 + s2));});return true;}return false;});if (!boole) {//多部门中某一个部门于list中不存在,则在集合中新增一个mapHashMap<String, Object> map = (HashMap<String, Object>)clone.clone();map.put("group_field_one",m);map.put("group_field_two",m);set.forEach(mSet -> {double s1 = Double.parseDouble(mSet.getValue().toString()) / dutyUnits.length;map.put(mSet.getKey(), nf.format(s1));});newAddList.add(map);}});});list.addAll(newAddList);//根据总计字段降序排序list.sort(Comparator.comparingDouble((t) -> -Double.parseDouble(t.get("counts").toString())));}
}

2.5 算法族具体的构建类

当需要增加新的策略时,只需新增一个ICompMonthlyStrategy 接口的实现类即可;

package com.icex.bus.sys.service.impl;import com.icex.bus.sys.dao.SysComplaintDao;
import com.icex.bus.sys.service.ICompMonthlyStrategy;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.*;/*** ICompMonthlyStrategy类具体的构建类-根据线路统计* @author Jjcc* @version 1.0.0* @description* @className CompMonthlyUnitImpl.java* @createTime 2019年09月09日 10:43:00*/
@Service("compMonthlyCircuitNameImpl")
public class CompMonthlyCircuitNameImpl implements ICompMonthlyStrategy {@Resourceprivate SysComplaintDao sysComplaintDao;/*** 根据线路统计方法* @title getCompMonthlyData* @description* @author Jjcc* @param condition* @return java.util.List<java.util.HashMap<java.lang.String,java.lang.Object>>* @createTime 2019/9/9 11:05* @throws*/@Overridepublic List<HashMap<String, Object>> getCompMonthlyData(Map<String, Object> condition) {List<HashMap<String, Object>> list = sysComplaintDao.selectMonthlyCompCircuitNameTj(condition);if (null != list && list.size() > 0) {//如果集合为空,则表示统计的数据没有,则不需要进行后续操作//用于字段中存在多个部门算法调用groupFieldMultiTwo(list);HashMap<String, Object> totalList  = sysComplaintDao.selectMonthlyCompTotalCircuitNameTj(condition);list.add(totalList);}return list;}
}

总结

在Spring项目中,使用策略模式。

定义一个算法族接口,实现不同的具体构建类,并用@Service("...")注册。

定义一个Context类,类中定义一个成员变量的集合;Map<String, 算法族接口类型>,并用@Autowired标记,Spring源码中@AutoWired注释大意为:当该注解标记于Map,List等集合时,会查找容器中所有注册的Bean并放入标记的集合中(通过集合的类型查找,Map的value为查找的类型;ps:如果是Map集合,查找到符合条件的Bean后,Bean的name是key,Bean本身是value);

可以通过工厂模式,枚举,反射等获得客户所需要的算法所在的Bean;

end!!!

Java SSM项目运用策略模式思想相关推荐

  1. java策略管理_详解Java编程中的策略模式

    策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以在不影响到客户端的情况下发生变化. 策略模式的结构 策略模式 ...

  2. java三层架构是不是策略模式,把「策略模式」应用到实际项目中

    无论你知不知道这个设计模式,但必定在项目中都似曾相识.倘若仅仅聊理论必然枯燥乏味,只有理论和实战相结合方可达到人剑合一的境界. 首先,我来说个需求,倘若是你遇到该如何做?你可停留几分钟,想出你的解决方 ...

  3. Java的设计模式----strategy(策略模式)

    设计模式: 一个程序员对设计模式的理解: "不懂"为什么要把很简单的东西搞得那么复杂.后来随着软件开发经验的增加才开始明白我所看到的"复杂"恰恰就是设计模式的精 ...

  4. Java设计模式之十一 ---- 策略模式和模板方法模式

    前言 在上一篇中我们学习了行为型模式的访问者模式(Visitor Pattern)和中介者模式(Mediator Pattern).本篇则来学习下行为型模式的两个模式,策略模式(Strategy Pa ...

  5. Java函数式实现替代策略模式解决 if...else代码,Map+函数式接口方法

    之前记录过用自定义注解和策略模式实现发不同消息的功能笔记: 文章地址:发送不同类型的消息----------策略模式_不受天磨非好汉,不遭人妒是庸才--着实着迷゛-CSDN博客r一:首先看下代码结构a ...

  6. vue+elementui学生宿舍管理系统(报修,来访登记,水电费)java ssm项目介绍

    宿舍,是大学生在高校校园里一个重要的学习.生活.交往的空间环境,大学生大约有2/3的时间是在宿舍环境里渡过的.作为计算机应用的一部分,使用计算机对宿舍信息进行管理,具有着手工管理所无法比拟的优点.例如 ...

  7. Java 8中的策略模式

    这是两个有关如何使用Java 8功能样式以及Cyclops模式匹配和Hamcrest库来实现策略模式设计的示例. PrintDependingOnInput方法是一种策略,该策略将根据传递的日志在Sy ...

  8. Java设计模式6:策略模式

    策略模式 策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立类中,从而使得它们可以相互替换.策略模式使得算法可以在不影响到客户端的情况下发生变化. 策略模式的结构 策略模式是对算法的包 ...

  9. 策略模式java 用例_java策略模式简单用例

    运用java策略模式一个小程序 /** * */ package Strategy; import java.util.Arrays; /** * @author HuangRong * @Funti ...

最新文章

  1. linux 僵尸进程 defunct
  2. java遍历集合选择题_Java集合知识测试B
  3. 【Python】一行python代码利用人工智能去除工作照背景
  4. SAP 电商云 Spartacus UI 产品明细页面路由路径的自定义配置
  5. MATLAB-电力电子技术仿-单向半波整流电路分析
  6. Numpy,Pandas,Matplotlib
  7. iOS 面试题分析(一)
  8. 假设检验_关于假设检验与P值的几点看法
  9. linux筛选方式,使用grep实现精确过滤的五种方法
  10. 步进电机驱动技术3:基于ULN2003的步进电机驱动
  11. JavaEE企业级实战项目 智牛股第一天 概要分析和环境搭建
  12. 信息奥赛课课通(C++)p139-例3幸运数的划分
  13. 团队开发如何评估工作量
  14. iPhone微信聊天记录误删怎么办?怎么恢复微信删除的记录
  15. STM32MP157-Linux音频应用编程-语音转文字项目
  16. 小程序关注公众号组件
  17. 如何借助SVG+CSS用2个小时撸完一个网易云音乐的动效海报(可控制速度)
  18. 协方差与皮尔森相关性系数
  19. Jira Bug severity(缺陷严重程度)说明
  20. 21、ZigBee 开发教程之基础篇—继电器模块

热门文章

  1. 回收站如何添加到桌面?没有回收站的数据怎么恢复
  2. Excel VBA 高级编程-来自直男的Excel表白
  3. Invalid character found in the request target. 的解决办法
  4. 虚拟机安装【kali】(2018/2021)
  5. 华尔街日报:Facebook视频广告可能让用户远离
  6. mysql高可用MHA
  7. 利用selenium爬取斗鱼漂亮小姐姐直播间信息
  8. Qt跨平台Linux5-使用DEFINES来实现条件编译
  9. 不光教女朋友学会了Yarn,还让她明白了Yarn与npm的区别
  10. BOSS卡上有两张SATA M.2 SSD,用来安装系统的。 BOSS卡介绍