需求
代码语言:javascript复制这里虚拟一个业务需求,让大家容易理解。假设有一个OA系统,里面的一个功能根据不
同的流程做不同的处理,比如有合同流程,请假流程,印章申请流程,出差申请。外出流程,报销流程等等很多。
传统实现
根据类型写一堆的if else:
public String handle(Process process){
if(process.type="1"){//请假}
else if(process.type="2"){//合同}
....
else{}
}
这样就会导致这个class/method很庞大,维护性很差。
使用策略模式实现
代码语言:javascript复制策略模式的重心:是如何实现算法,而是如何组织,调用算法,让程序更加灵活,具有更
好的可维护性和扩展性。
策略模式的本质:是算法分离,选择算法。
1.写一个抽象处理器来约束具体的实现
也可以定义成接口,定义成抽象类是因为有一些流程公共的方法可以放在抽象类中、
/**
* 抽象类,用来约束具体的处理流程
*/
public abstract class AbstractHandler {
abstract public String handle(String type);
}
2.写多个具体的实现(请假,合同)
/**
* 具体的流程类
*/
@Component
@HandlerType("1")
public class AgreementProcessHandler extends AbstractHandler{
@Override
public String handle(String type) {
System.out.printf("type" type);
return "处理合同流程success";
}
}
首先每个处理器都必须添加到spring容器中,因此需要加上@Component注解,其次需
要加上一个自定义注解@HandlerType,用于标识该处理器对应哪个流程,最后就是继
承AbstractHandler,实现自己的业务逻辑。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface HandlerType {
String value();
}
自定义注解和抽象处理器都很简单,那么如何将处理器注册到spring容器中呢?
具体思路是:
1、扫描指定包中标有@HandlerType的类;
2、将注解中的类型值作为key,对应的类作为value,保存在Map中;
3、以上面的map作为构造函数参数,初始化HandlerContext,将其注册到spring容器中;
我们将核心的功能封装在HandlerProcessor类中,完成上面的功能。
/**
* 扫描所有有HandlerType的注解的类,放入HandlerContext
* 将HandlerContext注册到spring中去
*/
@Component
public class HandlerProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Map<String,Class> handleMap = new HashMap<>();
Set<Class<?>> classSet = ClassScaner.scan("com.ding", HandlerType.class);
for(Class clazz : classSet){
HandlerType handlerType = (HandlerType) clazz.getAnnotation(HandlerType.class);
handleMap.put(handlerType.value(),clazz);
}
HandlerContext handlerContext = new HandlerContext(handleMap);
beanFactory.registerSingleton(HandlerContext.class.getName(),handlerContext);
}
}
核心工作已经完成,现在看看HandlerContext如何获取对应的处理器:
SpringContextHolder:获取bean工具类
#getInstance 方法根据类型获取对应的class,然后根据class类型获取注册到spring中的bean。
最后请注意一点,HandlerProcessor和SpringContextHolder必须能被扫描到,或者通过@Bean的方式显式的注册,才能在项目启动时发挥作用。
<bean class="com.ding.utils.SpringContextHolder"></bean>
<context:component-scan base-package="com.ding.handler" />
/**
* 上下文 用一个map来维护,获取所有有HandlerType的类
*/
public class HandlerContext {
private Map<String,Class> classHandlerMap;
public HandlerContext(Map<String, Class> classHandlerMap) {
this.classHandlerMap = classHandlerMap;
}
public AbstractHandler getInstance(String type){
Class clazz = classHandlerMap.get(type);
if(clazz ==null ){
throw new IllegalArgumentException("not found handler for type" type);
}
return (AbstractHandler) SpringContextHolder.getBean(clazz);
}
}
调用方式
代码语言:javascript复制/**
* 测试策略模式
*/
@SuppressWarnings("rawtypes")
@RequestMapping("/testMode")
@ResponseBody
public Result testMode(String type){
Result res = new Result();
AbstractHandler handler = handlerContext.getInstance(type);
handler.handle(type);
return res;
}
后续补充 讲的不错 https://zhuanlan.zhihu.com/p/91667659
参考 如何干掉 Spring Boot 中大片的 if else?