重构_使用模板模式替代重复业务操作
Posted fanyi0922
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重构_使用模板模式替代重复业务操作相关的知识,希望对你有一定的参考价值。
在日常开发中经常有重复的业务操作,每次写这些重复操作时总感觉冗余,又少点管理。今天我在开发中又遇到了这个问题,却发现项目组中的大牛已经帮我将重复的操作管理好了,于是便请教一番,偷偷学了这个---模板设计模式。
模板设计模式要注意以下几点:
-
将重复的业务操作抽取出一套模板;
-
将实现模板的实现类放到管理器中统一管理;
-
在业务中用的时候只用管理器,忽略模板的实现类。
掌握了要点之后,我们开始今天的实战阶段,项目中的需求是这样的:
-
先找到有某一类型的货品的日志表,日志表有,货品与日志表的对应关系也有(货品与日志表一一对应)
-
然后验证这个日志表中是否已经有核注信息(日志表的某一字段),返回结果;
-
这样的日志表大概有 8-10 张。
需求其实特别简单,每张表查一遍就完事了,但问题是相同的逻辑,要写 10遍吗 ?如果多加一张表,还要改动主流程的逻辑吗?有没有更好的方法呢?
下面我们将重复的工作模板化,首先拆出一个模板类:
/** * 抽象模板,定义所有 processor 的处理逻辑 * * @author fanpengyi * @version 1.0 * @date 2019-7-28 */ public abstract class AbstractTemplateProcessor<T> implements DataSupplier<T>, InitializingBean //所有的商品类型 private Set<CertType> types; public AbstractTemplateProcessor() this.types = getTypes(); /** * 抽象模板的执行方法 * * @param paramList 业务层传过来的需要的?参数 * @param types 业务层传过来d额商品类型 */ public Response<Void> process(ParamList paramList, Set<CertType> types) //获取参数 T supply = supply(paramList); //process的判断 if (accept(supply, types)) //真正执行 process return doProcess(paramList.getUseId()); return Response.success(); /** * 判断 process 执行方法的前置判断 * * @param projectCode 商品编号 * @param types 商品类型? * @return */ protected abstract boolean accept(T projectCode?, Set<CertType> types); /** * 真正执行 process 方法 * * @param bizId? */ protected abstract Response<Void> doProcess(String biz?Id); /** * 获取每个 processor 支持的类型 * * @return */ public abstract Set<CertType> getTypes(); /** * 加载类的后置处理,将processor注册到?处理器中 * * @throws Exception */ @Override public void afterPropertiesSet() throws Exception TemplateProcessorMananger.getInstance().register(this);
/** * 用于向抽象模板中传递需要的参数 * @author fanpengyi * @version 1.0 * @date 2019-7-28 */ public interface DataSupplier<T> /** * 根据 paramList 获取每个 processor 需要处理对象 * @param paramList 参数列表 * @return */ T supply(ParamList paramList);
由模板类最后可知,需要一个 processor 的管理器,管理器需要注意几点:
-
管理器必须是单例的
-
管理器的操作都需要加锁,保证线程安全
/** * process 管理器 用于注册 process 与 执行所有的 process * @author fanpengyi * @version 1.0 * @date 2019-7-28 */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class TemplateProcessorMananger /** *静态的引用,单例 */ @Getter private static TemplateProcessorMananger Instance = new TemplateProcessorMananger(); //注册管理器,统一放置 process,如果有顺序 需要注意顺序 private List<AbstractTemplateProcessor> processors = new LinkedList<>(); private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); /** * 注册方法 */ public void register(AbstractTemplateProcessor processor) try //加锁是保证安全,不然可能报错 CopyOnWriteList 是线程安全的 lock.writeLock().lock(); processors.add(processor); finally lock.writeLock().unlock(); public List<Response<Void>> process(ParamList paramList, Set<CertType> types) try //加锁是为了在读的时候不允许其他线程操作 processors lock.readLock().lock(); return processors.stream() .map( o-> (Response<Void>)o.process(paramList,types)) .collect(Collectors.toList()); finally lock.readLock().unlock();
除了以上的模板类和管理器外,还需要定义一个全局的参数,保存每个具体的 processor 的结果,由于是并发访问,不能保证每次访问的商品类型一致,所以此处我们采用 ThreadLocal 的形式,逐级传递,保证每个线程参数的安全性,而全局参数的初始化与销毁到放在了拦截器中实现。
/** * 每个线程私有的变量,类似于全局变量,但是每个线程自己有自己的一份 * 放在 拦截器中初始化 和 销毁,有初始化,有销毁,不然会有内存泄漏的风险 * * @author fanpengyi * @version 1.0 * @date 2019-7-28 */ public class DataHolder private static ThreadLocal<ParamList> threadLocal; /** * 初始化 theadLocal */ public static void start() if(threadLocal == null) threadLocal = new ThreadLocal<>(); if(threadLocal.get() == null) threadLocal.set(new ParamList()); public static ParamList get()return threadLocal.get(); public static void update(ParamList paramList)threadLocal.set(paramList); public static void shutdown() if(threadLocal != null) threadLocal.remove();
/** * 拦截器中初始化 和 销毁 threadLocal * @ConditionalOnClass(Controller.class) -> 执行条件 有Controller 类时执行 * @author fanpengyi * @version 1.0 * @date 2019-7-28 */ @Configuration @ConditionalOnClass(Controller.class) public class WebMvcConfig implements WebMvcConfigurer @Override public void addInterceptors(InterceptorRegistry registry) registry.addInterceptor(new HandlerInterceptor() @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception DataHolder.start(); return true; @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception //ingore 不做处理 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception DataHolder.shutdown(); );
有了以上三个核心类,我们只需要创建抽象模板的实现类,每个 processor 实现自己的方法,互不影响,最终都会被注册进管理器中,在业务方法中调用管理器的 process 方法就可以达到根据商品类型不同调用不同的processor 的目的了。
/** * 业务方法 */ public void doTemplate() // 准备参数 ParamList paramList = new ParamList(); paramList.setUseId("6666"); Set<CertType> sets = new HashSet<>(); sets.add(CertType.TYPE_3); sets.add(CertType.TYPE_1); TemplateProcessorMananger.getInstance().process(paramList,sets); //预期 只会执行 1 ,3的processor Map<String, Object> resultMaps = DataHolder.get().getResultMaps(); for (String s : resultMaps.keySet()) log.info(s+"------>"+resultMaps.get(s));
使用模板模式替代重复的业务操作,优点在于将重复操作统一管理,添加新的业务时对主流程侵入小,易于扩展,易于管理。
参考资料:
https://github.com/fanpengyi/template
---- 文中代码git库
以上是关于重构_使用模板模式替代重复业务操作的主要内容,如果未能解决你的问题,请参考以下文章