使用策略模式干掉大片的 if else

2022-08-12 20:43:17 浏览数 (1)

需求

代码语言: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?

0 人点赞