• 重构_使用模板模式替代重复业务操作


    在日常开发中经常有重复的业务操作,每次写这些重复操作时总感觉冗余,又少点管理。今天我在开发中又遇到了这个问题,却发现项目组中的大牛已经帮我将重复的操作管理好了,于是便请教一番,偷偷学了这个---模板设计模式。

    模板设计模式要注意以下几点:

    1. 将重复的业务操作抽取出一套模板;

    2. 将实现模板的实现类放到管理器中统一管理;

    3. 在业务中用的时候只用管理器,忽略模板的实现类。

    掌握了要点之后,我们开始今天的实战阶段,项目中的需求是这样的:

    • 先找到有某一类型的货品的日志表,日志表有,货品与日志表的对应关系也有(货品与日志表一一对应)

    • 然后验证这个日志表中是否已经有核注信息(日志表的某一字段),返回结果;

    • 这样的日志表大概有 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库

  • 相关阅读:
    HTML5定位功能,实现在百度地图上定位
    很好用的API管理--Swagger
    依赖注入原理---IoC框架
    c# asp.net 修改webconfig文件 配置
    逗比的快钱支付
    MVC 5限制所有HTTP请求必须是POST方式
    富头像上传编辑器----很好用的上传照片和拍照功能
    .NET中的DES对称加密
    无聊搭1个框架
    PHP高效率写法
  • 原文地址:https://www.cnblogs.com/fanyi0922/p/11261268.html
Copyright © 2020-2023  润新知