由于业务中经常有需要判断的if--eles操作,层层嵌套,看起来程序的可读性太差,结合策略模式进行改造

方法一、一般有策略模式  +  工厂模式进行代码的优化,减少 if---else;

方法二、还有就是利用策略模式  +  SpringBoot提供的某些类  进行包装

本次介绍采用方法二的方式,大概的思路是:

1、策略模式:将所有同类型的操作抽象出来一个接口(这个接口包含一个动作方法) 和 一个实现了接口的抽象类(不实现方法);
2、根据需求,将同类型的操作抽象成一个一个产品,继承第一步的抽象类,并实现抽象方法,编写自己的业务逻辑,注意此类需要注入spring容器;
3、自定义一个类级别注解,用来区分不同操作类型的标识,此自定义标识要有返回一个类型的属性;
4、抽象出来一个处理所有产品的公共HandlerContext对象,此对象提供一个获取具体产品类的方法,该方法有个入参用于表明是具体那个产品,同时该HandlerContext对象还具有Map类型的属性变量,
存储key为具体的类型,value为具体的产品类对象,该Map对象通过构造函数的方式注入初始化进来;
5、编写一个加载所有产品类的全局process类,用于扫描加了注解@HandlerType的所有实现产品,给存储key 和 value产品对象Map赋值,初始化HandlerContext 将其注册到spring容器中;

需求

这里虚拟一个业务需求,让大家容易理解。假设有一个订单系统,里面的一个功能是根据订单的不同类型作出不同的处理。

订单实体:

service接口:

传统实现

根据订单类型写一堆的if else:

下面采用方法二来进行优化:

1、策略模式:

将所有同类型的操作抽象出来一个接口(这个接口包含一个动作方法) 和 一个实现了接口的抽象类(不实现方法);

先定义一个数据传输的实体类DTO     OrderDTO

 @Data
public class OrderDTO { private String code; private BigDecimal price; /**
* 订单类型
* 1:普通订单
* 2:团购订单
* 3:促销订单
*/
private String orderType;
}

定义一个抽象类的接口:

IHandlerService
 /**
* <p>Title: com.aier.cloud.biz.simplify</p>
* <p>Company:爱尔集团信息中心</p>
* <p>Copyright:Copyright(c)</p>
* User: duanm
* Date: 2019/10/31 15:33
* Description: No Description
*/
public interface IHandlerService { String handler(OrderDTO orderDTO);
}

AbstractHandlerService:抽象类

 public abstract class AbstractHandlerService  implements IHandlerService {

     abstract public String handler(OrderDTO orderDTO);

 }

2、根据需求,将同类型的操作抽象成一个一个产品,继承第一步的抽象类,并实现抽象方法,编写自己的业务逻辑,注意此类需要注入spring容器;

团购订单处理类:

 @Component
@HandlerType(value = "2")
public class GroupHandler extends AbstractHandlerService { @Override
public String handler(OrderDTO orderDTO) {
return "处理团购订单";
}
}

普通订单处理类:

 @Component
@HandlerType(value = "1")
public class NormalHandler extends AbstractHandlerService { @Override
public String handler(OrderDTO orderDTO) {
return "处理普通订单";
}
}

促销订单处理类:

 @Component
@HandlerType(value = "3")
public class PromotionHandler extends AbstractHandlerService {
@Override
public String handler(OrderDTO orderDTO) {
return "处理促销订单";
}
}

注意事项:必须添加  @Component  注解,注入Spring容器      下面编写自定义的注解实现  HandlerType

3、自定义一个类级别注解,用来区分不同操作类型的标识,此自定义标识要有返回一个类型的属性;

 @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface HandlerType {
String value();
}

4、 抽象出来一个公共HandlerContext对象

抽象出来一个处理所有产品的公共HandlerContext对象,此对象提供一个获取具体产品类的方法,该方法有个入参用于表明是具体那个产品,同时该HandlerContext对象还具有Map类型的属性变量,
存储key为具体的类型,value为具体的产品类对象,该Map类型变量初始化通过构造函数的方式注入;

 public class HandlerContext {

     private Map<String, Class> handlerMap;

     public HandlerContext(Map<String, Class> handlerMap) {
this.handlerMap = handlerMap;
} public AbstractHandlerService getInstance(String type) {
Class clazz = handlerMap.get(type);
if (clazz == null) {
throw new IllegalArgumentException("not found handler for type : " + type);
}
return (AbstractHandlerService) BeanTool.getBean(clazz);
}
}

5、编写一个加载所有产品类的全局process类,用于扫描加了注解@HandlerType的所有实现产品,给存储key 和 value产品对象Map赋值,初始化HandlerContext 将其注册到spring容器中;

 @Component
public class HandlerProcessor implements BeanFactoryPostProcessor { private static final String HANDLER_PACKAGE = "com.aier.cloud.biz.simplify"; /**
* 扫描@HandlerType,初始化HandlerContext 将其注册到spring容器中
*
* @param configurableListableBeanFactory
* @throws BeansException
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
Map<String, Class> handlerMap = Maps.newHashMap(); ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AnnotationTypeFilter(HandlerType.class));
Set<BeanDefinition> candidateComponents = provider.findCandidateComponents(HANDLER_PACKAGE);
candidateComponents.forEach(Beanclass -> {
try {
Class<?> clazz = Class.forName(Beanclass.getBeanClassName());
//获取注解中的类型值
String type = clazz.getAnnotation(HandlerType.class).value();
//将注解中的类型值做为key,对应的类作为value 保存在handlerMap中
handlerMap.put(type, clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}); //初始化HandlerContext类,将其注入到Spring容器中
HandlerContext handlerContext = new HandlerContext(handlerMap);
configurableListableBeanFactory.registerSingleton(HandlerContext.class.getName(), handlerContext); }
}

注意:主要使用了spring的资料加载工具类,把所有的产品实现类都扫描 存储到map中,并利用继承  BeanFactoryPostProcessor   通过实现它的方法,动态的注入 HandlerContext 进入spring容器

自定义注解和抽象处理器都很简单,那么如何将处理器注册到spring容器中呢?

具体思路是:

1、扫描指定包中标有@HandlerType的类;

2、将注解中的类型值作为key,对应的类作为value,保存在Map中;

3、以上面的map作为构造函数参数,初始化HandlerContext,将其注册到spring容器中;

几个关键的工具类:

 @Component
public class SpringContextUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
} /**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
checkApplicationContext();
return applicationContext;
} /**
* 清除applicationContext静态变量.
*/
public static void cleanApplicationContext() {
applicationContext = null;
} private static void checkApplicationContext() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");
}
} /**
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
checkApplicationContext();
return (T) applicationContext.getBean(name);
} /**
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(Class<T> clazz) {
checkApplicationContext();
return (T) applicationContext.getBeansOfType(clazz);
}
}
 public class BeanTool {

     public static  <T> T getBean(Class<T> clazz) {
String clazzName = clazz.getName();
clazzName = clazzName.replace(".",",");
String[] split = clazzName.split(",");
String name = split[split.length - 1];
return SpringContextUtils.getBean(lowerFirst(name));
} public static String lowerFirst(String oldStr) {
char[] chars = oldStr.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
}

注意此处可以优化下,可以简单来获取类的名字,

 public static  <T> T getBean(Class<T> clazz) {
//获取类的名字
String simpleName = clazz.getSimpleName();
//根据类的名称获取类的实列对象
return SpringContextUtils.getBean(lowerFirst(simpleName));
}

思考:  还是JAVA反射 不太熟悉,导致走了弯路来处理,JAVA反射还是要吃透,多写写。

(1)反射机制极大的提高了程序的灵活性和扩展性,降低模块的耦合性,提高自身的适应能力。

(2)通过反射机制可以让程序创建和控制任何类的对象,无需提前硬编码目标类。
(3)使用反射机制能够在运行时构造一个类的对象、判断一个类所具有的成员变量和方法、调用一个对象的方法。
(4)反射机制是构建框架技术的基础所在,使用反射可以避免将代码写死在框架中。

6、测试运行代码,查看结果

测试运行:在service层进行调用,可以如下来编写:

 @Service
public class OrderServiceImpl implements IOrderService { @Resource
private HandlerContext handlerContext; @Override
public String handle(OrderDTO orderDTO) {
IHandlerService instance = handlerContext.getInstance(orderDTO.getOrderType());
System.out.println(instance.handler(orderDTO));
return instance.handler(orderDTO);
}
}

运行结果如下:

最后请注意一点,HandlerProcessor和BeanTool必须能被扫描到,或者通过@Bean的方式显式的注册,才能在项目启动时发挥作用。

总结

利用策略模式可以简化繁杂的if else代码,方便维护,而利用自定义注解和自注册的方式,可以方便应对需求的变更。本文只是提供一个大致的思路,还有很多细节可以灵活变化,例如使用枚举类型、或者静态常量,作为订单的类型,相信你能想到更多更好的方法。

2、后续追加更优雅处理

针对实现了策略模式的具体操作类,在业务处理类中,可以通过spring的已有功能进行处理,怎么通过不同的类型,获取到该类型的实现类。

通过业务类的构造方法,

hashMap  hp = new hashMap();// 最好采用线程安全的MAP对象,需要优化

@Autowired
public AemrMessageServiceImpl(List<策略模式的接口对象参数>){
// 初始化map,存储起来 map.put(业务类型key,业务类型实现)
}

思路:

1、通过spring的IOC快速实现通过类型type注入这个type的所有实现;

2、通过申明一个线程安全的Map初始化数据,在业务类的构造函数中初始化进来,map.put(业务类型,策略模式业务的具体实现类);

3、在具体使用的业务类处理方法中,通过类型获从map中拿到具体的实现类型,完成调用;

springBoot中怎么减少if---else,怎么动态手动注册类进入Spring容器的更多相关文章

  1. SpringBoot 之 普通类获取Spring容器中的bean

    [十]SpringBoot 之 普通类获取Spring容器中的bean   我们知道如果我们要在一个类使用spring提供的bean对象,我们需要把这个类注入到spring容器中,交给spring容器 ...

  2. [十]SpringBoot 之 普通类获取Spring容器中的bean

    我们知道如果我们要在一个类使用spring提供的bean对象,我们需要把这个类注入到spring容器中,交给spring容器进行管理,但是在实际当中,我们往往会碰到在一个普通的Java类中,想直接使用 ...

  3. Spring Boot中普通类获取Spring容器中的Bean

    我们知道如果我们要在一个类使用spring提供的bean对象,我们需要把这个类注入到spring容器中,交给spring容器进行管理,但是在实际当中,我们往往会碰到在一个普通的Java类中,自己动手n ...

  4. SpringBoot中并发定时任务的实现、动态定时任务的实现(看这一篇就够了)

    原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10659045.html,否则将追究法律责任!!! 一.在JAVA开发领域,目前可以通过以下 ...

  5. elastic-job 分布式定时任务框架 在 SpringBoot 中如何使用(二)动态添加任务需求

    之前一篇用过了如何在使用创建最简单的任务:比如每天定时清空系统的缓存 这篇文章主要讲解:如何运用elastic-job-lite做灵活的细粒度任务,比如: 如何定时取消某个订单在下订单后30分钟未支付 ...

  6. 使用反射获取类中的属性(可用于动态返回PO类的列,当做表格的表头)

    //利用反射取类中的属性字段 try { Class clazz = Class.forName("houji.bean.model.TaskModel"); Field[] fi ...

  7. SpringBoot之普通类获取Spring容器中的bean

    package com.geostar.geostack.git_branch_manager.common; import org.springframework.beans.BeansExcept ...

  8. FastJson序列化Json自定义返回字段,普通类从spring容器中获取bean

    前言: 数据库的字段比如:price:1 ,返回需要price:1元. 这时两种途径修改: ① 比如sql中修改或者是在实体类转json前遍历修改. ②返回json,序列化时候修改.用到的是fastj ...

  9. Spring Retry 在SpringBoot 中的应用

    Spring Boot中使用Spring-Retry重试框架 Spring Retry提供了自动重新调用失败的操作的功能.这在错误可能是暂时的(例如瞬时网络故障)的情况下很有用. 从2.2.0版本开始 ...

随机推荐

  1. 看一下我学习linux的过程

    学习Linux的最佳方法是将它用于日常工作. 阅读Linux书籍,观看Linux视频不仅仅是足够的. 学习Linux没有捷径可走. 你不可能在一夜之间在Linux中掌握. 这需要时间和持久性. 刚刚潜 ...

  2. Hadoop_13_Hadoop Shell脚本采集日志上传到HDFS

    案例1:开发shell采集脚本 1.点击流日志每天都10T,在业务应用服务器上,需要准实时上传至数据仓库(Hadoop HDFS)上 2.一般上传文件都是在凌晨24点操作,由于很多种类的业务数据都要在 ...

  3. Hadoop_01_Apache Hadoop概述

    一:Hadoop(Hadoop Distributed File System)概述:对海量数据分析处理的工具 1. Hadoop是Apache旗下的一个用java语言实现开源软件框架,是一个开发和运 ...

  4. 团队第三次作业:Alpha版本第一周小结

    姓名 学号 周前计划安排 每周实际工作记录 自我打分 XXX 061109 1.原型设计与编码任务分配 2.构思程序个性化测试模块的初步实现 1.原型设计与编码任务分配 2.设计了部分类及其成员函数( ...

  5. 图像处理---《在图片上打印文字 putText()》

    图像处理---<在图片上打印文字 putText()> 目的:想在处理之后的图像上打印输出结果. 方法: (1)只在图像上打印 数字.字母的话:                 1.Mat ...

  6. PHP 按照指定数量分割数组

    <?php /** * 系统辅助类 * @date 2019年7月2日 * @comment * */ class SystemUtils { private static $_instance ...

  7. 在MyEclipse10中使用Maven

    虽然很多人说maven比起gradle来已经又落后了,但还是有必要了解一下的. 这两天看了好多文章,也跟着做了很多例子,无一例外,创建的pom.xml文件都是有错的.而且由于使用的开发工具不一致,导致 ...

  8. 《黑白团团队》第九次团队作业:Beta冲刺第二天

    项目 内容 作业课程地址 任课教师首页链接 作业要求 团队项目 填写团队名称 黑白团团队 填写具体目标 认真负责,完成项目 团队项目Github仓库地址链接. 第一天 日期:2019/6/24 1.1 ...

  9. 回顾了下shell 编程的一些细节

    回顾了一些细节 shell 的通配符扩展   *  ? []  [^xxxx]  {aaa,bbb}         子进程 more `grep -l POSIX *` more $(grep -l ...

  10. 软raid实现

    几种raid类型,我就不在这里赘述了,企业一般都是用raid卡,所以一般使用软raid的非常少,但是也有用的,所以就写一个软raid的实验吧,其实用处不大. 实验环境:centos6.9 需要的硬件: ...