• 模板模式+策略模式优化


    前言

    写在最前面,最近在项目中发现一部分代码,可以用模板模式+策略模式来进行优化。

    背景

    业务中,系统中对订单状态的处理,需要发送对用户短信发送或者微信推送等操作。

    发送短信和微信推送钱,需要处理逻辑,比如获取模板,可能还有查询逻辑,或者一些扩展字段逻辑处理等
    在发送和推送之后,可能需要记录日志等,有可能不需要。

    在优化之前我们的代码是这样

    //这里写伪代码
    // -----  业务代码
    // 创单单未支付
    
    1 发送短信,
    2 发送推送
    3 记录日志
    
    ///  业务 代码
    //创单成功
    
    1 发送短信,
    2 发送推送
    3 记录日志
    
    //支付成功 等等
    
    //取消  等等
    1 发送短信,
    2 发送推送
    3 记录日志
    
    
    

    这样的 代码很多,对此如取消订单入口很多,比如后台客服取消,客户端 客户取消,还有超时取消,供应商取消等。

    那我们每个地方都写一遍上述代码,发短信,推送?

    显然是可以的,但存在以前几个问题

    1 这样代码重复率很高
    2 非异步执行(请求很多会影响吞吐量)
    3 多处修改

    优化

    方案 1

    将发送短信,推送短信全部剥离抽取到一个方法里,在调用的地方全部调用一下。

    
    class Utile{
    
    static void doSend(){
    //伪代码
    
    if(创单未支付){
    //业务代码
    }else if(取消){
    //业务代码
    }
    // 其他状态省略
    
    }
    
    }
    
    
    

    调用地方

    
    Utile.doSend()
    
    

    但是这样还是同步,每个地方还是会用重复,还是会有同步调用问题,同样业务量大的时候还是会影响吞吐量。

    方案 2 (增加异步线程发送 )

    增加异步线程发送

    
    class Utile{
    
    
        private static final AtomicInteger atomicInteger = new AtomicInteger();
    
        public static final ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(500), (r) -> {
            Thread thread = new Thread(r);
            thread.setName("ExecutorUtil-pool-send-push" + atomicInteger.getAndIncrement());
            thread.setUncaughtExceptionHandler((t, e) -> {
                loggerHelper.errorLog(t.getName() + " 线程处理异常 : " + e.getMessage(),new RuntimeException(e));
                e.printStackTrace();
            });
            return thread;
        }, new ThreadPoolExecutor.AbortPolicy());
    
    static void doSend(int orderType){
    
    //这里使用线程池
    
    
       executor.execute(()->{
                //伪代码
    
    		if(创单未支付){
    		//业务代码
    		}else if(取消){
    		//业务代码
    		}
    		// 其他状态省略          
    });
    
    
    }
    
    
    

    使用线程池也存在几个问题

    • 1 首先线程数量选择多少 线程数量占用多也不好,会影响业务线程
    • 2 队列容量多大?
    • 3 队列满了 怎么办?
    • 4 发送失败怎么办,发短信和推送也不是很重要的业务,可以忽略

    业务量少的时候,这种解决办法也是可以的。但不太适合大的业务量。

    方案 2 (mq + 设计模式 处理 )

    这里我们不关心mq的处理,前提是已经放入mq 这里我们直接默认从mq拿。

    重点是设计模式,设计的思想。

    基于上面公用类 Utile ,处理和发送短信和 消息

    如果后期随着业务量的增加或者推送方推送或者发送短信,对此,在公用类中进行添加if判断进行处理?
    业务中如果发送和推送也需要记录日志,不同的方式记录不同的日志?

    原方法

    
    // 代码进行了 部分伪代码处理
        public void mqSendWXMessage(WxOrderNoBean wxOrderNoBean) {
    
            // -----------------  -  微信推送 -----------------------
            if (wxOrderNoBean.getType() == WxPushTypeEnum.COMPLETE_CHECK_TICKET.getValue()) {
               // 业务逻辑......
                
                //微信推送
                orderDetailInfoList.forEach(detailInfo -> wechatTemplateSettingService.wechatMessageForCompleteCheck(detailInfo));
                return;
            }else if (wxOrderNoBean.getType() == WxPushTypeEnum.CANCEL_to_supplier.getValue()){
                // 业务逻辑......
                        loggerHelper.infoLog("工作人员列表:" + JSON.toJSONString(stewardList), wxOrderNoBean.getOrderNo());
                        //微信推送 工作人员
                        WechatMessageUtil.cancelOrderInfoToSupplier(stewardList, validOrderInfoList.get(0));
                    
                    return;
               
            }
            
            List<RideOrderInfo> validOrderInfoList = rideOrderInfoDao.findByOrderNos("", Arrays.asList(wxOrderNoBean.getOrderNo()));
            List<RideOrderInfo> wxOrderInfoList = validOrderInfoList.stream().filter(item -> (item.getStoreId() == null || item.getStoreId() == 0)).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(wxOrderInfoList)) {
                return;
            }
            RideOrderInfo rideOrderInfo = wxOrderInfoList.get(0);
            WechatPaySetting paySetting = wechatPaySettingDao.findByVcode(rideOrderInfo.getVcode());
            WechatTemplateSetting templateSetting= null;
            int logSataus = 0;
            int logType = 0;
            String remark = "";
    
            if (wxOrderNoBean.getType() == WxPushTypeEnum.USER_CANCEL.getValue()){  //用户取消
                // 业务逻辑......
                WechatMessageUtil.cancelOrderToCustomer(rideOrderInfo, paySetting, templateSetting, refundFee, chargeFee);
                remark = "用户取消订单推送";
                logType =  OrderLogStatus.REFUND_USER.getValue();
    
                // 商家取消
            }else if (wxOrderNoBean.getType() == WxPushTypeEnum.SUPPLIER_CANCEL.getValue()){
                // 业务逻辑......
               
                WechatMessageUtil.ticketFailOrderToCustomer(rideOrderInfo, paySetting, templateSetting);
                remark = "商家取消订单推送";
                logType =  OrderLogOptType.NOT_SERVICE_OPT.getValue();
                logSataus =  OrderLogStatus.CANCELED_SUPPLY.getValue();
                // 非客服操作 出票成功
            }else if(wxOrderNoBean.getType() == WxPushTypeEnum.TICKET_OUT.getValue()){
                // 业务逻辑......
                //判断是否派车
                if (driverChangedMap!=null && driverChangedMap.get(rideOrderInfo.getOrderNo())) {
                    logType = OrderLogStatus.SENTED.getValue();
                } else {
                    logType = OrderLogStatus.CONFIRMED.getValue();
                }
    
                List<OrderPassenger> orderPassengerList = orderPassengerDao.getOrderPassengers(rideOrderInfo.getOrderNo());
                WechatMessageUtil.addOrderToCustomer(rideOrderInfo, paySetting, templateSetting, orderPassengerList);
                remark = "订单出票成功推送";
                logSataus = OrderLogStatus.REBOOK_SUCCESS.getValue();
    
                //非客服 改签推送
            }else if(wxOrderNoBean.getType() == WxPushTypeEnum.ORDER_REBOOK_SUCCESS.getValue()){
                // 业务逻辑......
               
                
                List<OrderPassenger> orderPassengerList = orderPassengerDao.getOrderPassengers(rideOrderInfo.getOrderNo());
                WechatMessageUtil.rebookOrderToCustomer(rideOrderInfo, orderPassengerList, oldShiftSchedule, paySetting, templateSetting);
    
                logSataus = OrderLogStatus.REBOOK_SUCCESS.getValue();
                remark = "改签订单推送";
                logType = OrderLogOptType.NOT_SERVICE_OPT.getValue();
            }else {
                return;
            }
    
            //记录日志
            rideOrderLogService.wxLogSave(new RideOrderLog(rideOrderInfo.getVcode(), rideOrderInfo.getOrderNo(),logSataus , remark, logType,
                    rideOrderInfo.getTelephone(), Constants.PushChannel.WECHAT_CHANNEL, Constants.PushType.SYSTEM_PUSH, "SYSTEM"));
    

    那这个方法随着改动会越来越混乱,对此,这里我们可以使用模板模式 + 策略模式来进行处理。

    设计思想

    首先,对具体方法进行抽象。

    消息处理接口

    public interface Message {
    
        void  dealMqMessage(WxOrderNoBean wxOrderNoBean);
    
    }
    
    

    不管是发送还是推送,可以分为事前操作, 发送or推送,事后操作,具体的操作让子类去实现。
    以及类型区分也让子类去实现

    抽象出来类

    public abstract class AbstractDealMessage implements Message {
    
        @Override
        public void dealMqMessage(WxOrderNoBean wxOrderNoBean) {
            befor(wxOrderNoBean);
            doDeal(wxOrderNoBean);
            after(wxOrderNoBean);
        }
    
    
        public abstract void befor(WxOrderNoBean wxOrderNoBean);
    
        public abstract void doDeal(WxOrderNoBean wxOrderNoBean);
    
        public abstract void after(WxOrderNoBean wxOrderNoBean);
    
    
    
        public abstract int getType();
    
        public abstract   Class<?>  getClasz();
    }
    
    

    公共的逻辑处理实现

    公共类里可以增加,日志类型,日志状态,日志备注,一些模板业务查询 bean ,是否需要处理下一步的 flag

    这里将一些公共的行为,do操作,操作前,操作后让父类(也就是当前类)实现,差异以及类型让子类去实现。

    
    
    public abstract class AbstractDealWxMqMessage extends AbstractDealMessage {
    
        private LoggerHelper loggerHelper = LoggerHelper.getLoggerHelper(xxxxxxxx, AbstractDealWxMqMessage.class.getSimpleName());
    
    //订单查询
        private RideOrderInfoDao rideOrderInfoDao;
    
    //日志记录bean
        private RideOrderLogService rideOrderLogService;
    
        private WechatPaySettingDao wechatPaySettingDao;
    
    //模板查询
        private WechatTemplateSettingService wechatTemplateSettingService;
    
    //模板查询
        private WechatTemplateSetting  templateSetting;
    
    //订单
        private RideOrderInfo rideOrderInfo;
    
    //微信模板
        private WechatPaySetting paySetting;
    
    // 日志 备注
        private String remark;
    
    //日志类型
        private int logType;
    
    // 日志状态
        private int logSataus;
    
        //是否继续执行
        private boolean isDo;
    
        public void setDo(boolean aDo) {
            isDo = aDo;
        }
    
        public RideOrderInfo getRideOrderInfo() {
            return rideOrderInfo;
        }
    
        public WechatPaySetting getPaySetting() {
            return paySetting;
        }
    
        public WechatTemplateSetting getTemplateSetting() {
            return templateSetting;
        }
    
    
        public void setRemark(String remark) {
            this.remark = remark;
        }
    
        public void setLogType(int logType) {
            this.logType = logType;
        }
    
        public void setLogSataus(int logSataus) {
            this.logSataus = logSataus;
        }
    
        public AbstractDealWxMqMessage(RideOrderLogService rideOrderLogService, WechatPaySettingDao wechatPaySettingDao,
                                       WechatTemplateSettingService wechatTemplateSettingService
                , RideOrderInfoDao rideOrderInfoDao) {
            this.rideOrderLogService = rideOrderLogService;
            this.wechatPaySettingDao = wechatPaySettingDao;
            this.wechatTemplateSettingService = wechatTemplateSettingService;
            this.rideOrderInfoDao = rideOrderInfoDao;
            this.isDo = true;
        }
    
    
        @Override
        public void befor(WxOrderNoBean wxOrderNoBean) {
    
            List<RideOrderInfo> validOrderInfoList = rideOrderInfoDao.findByOrderNos("", Arrays.asList(wxOrderNoBean.getOrderNo()));
            if (CollectionUtils.isEmpty(validOrderInfoList)) {
                isDo = false;
                return;
            }
    
            rideOrderInfo = validOrderInfoList.get(0);
            paySetting = wechatPaySettingDao.findByVcode(rideOrderInfo.getVcode());
    
        }
    
    
        public boolean findTemplateSetting(int templateType) {
            if (!isDo) {
                return false;
            }
    
            templateSetting = wechatTemplateSettingService.findActiveByTemplateType(rideOrderInfo.getVcode(), templateType);
            if (null == templateSetting) {
                loggerHelper.infoLog(" 暂无微信推送模板信息 ");
                isDo = false;
                return false;
            }
    
            return true;
        }
    
        @Override
        public void after(WxOrderNoBean wxOrderNoBean) {
    
    //是否需要记录日志 判断
            if (!isDo) {
                return;
            }
    
            rideOrderLogService.wxLogSave(
                    new RideOrderLog(
                            rideOrderInfo.getVcode(),
                            rideOrderInfo.getOrderNo(),
                            logSataus,
                            remark,
                            logType,
                            rideOrderInfo.getTelephone(),
                            Constants.PushChannel.WECHAT_CHANNEL,
                            Constants.PushType.SYSTEM_PUSH,
                            "SYSTEM")
            );
        }
    
    
    
    }
    
    

    最后不同的业务类实现,这里我们列出一个即可,其他的大体相同

    比如说取消推送

    
    /**
     * 用户取消 USER_CANCEL
     */
    @Component
    @Scope("prototype")  //这里为了防止线程安全,我们这里使用多例。 当然自己也可以去其他方法处理
    public class UserCancelMqMessage extends AbstractDealWxMqMessage {
    
    
    //注入 需要的对象bean
        @Autowired
        public UserCancelMqMessage(RideOrderLogService rideOrderLogService, WechatPaySettingDao wechatPaySettingDao, WechatTemplateSettingService wechatTemplateSettingService, RideOrderInfoDao rideOrderInfoDao) {
            super(rideOrderLogService,wechatPaySettingDao,wechatTemplateSettingService, rideOrderInfoDao);
        }
    
    
    //真的差异处理, 处理一些差异(扩展)字段,是否需要执行下一步记录日志等  ,这里在上面父类中将推送的模板信息已经查了
        @Override
        public void doDeal(WxOrderNoBean wxOrderNoBean) {
    
    //调用父类 查询模板
            if (!super.findTemplateSetting(TemplateTypeEnum.USER_CANCEL.getValue())) {
                return;
            }
    
            RideOrderInfo rideOrderInfo = super.getRideOrderInfo();
            WechatPaySetting paySetting = super.getPaySetting();
            WechatTemplateSetting templateSetting = super.getTemplateSetting();
    
            Map<String, String> map = JSON.parseObject(wxOrderNoBean.getExtensionField(), Map.class);
            String refundFee = map.get("refundFee");
            String chargeFee = map.get("chargeFee");
    
            WechatMessageUtil.cancelOrderToCustomer(rideOrderInfo, paySetting, templateSetting, refundFee, chargeFee);
    
    //设置类型  属性  备注
            super.setRemark("用户取消订单推送");
            super.setLogType(OrderLogOptType.NOT_SERVICE_OPT.getValue());
            super.setLogSataus(OrderLogStatus.REFUND_USER.getValue());
    
        }
    
        @Override
        public int getType() {
            return WxPushTypeEnum.USER_CANCEL.getValue();
        }
        @Override
        public Class<?> getClasz() {
            return UserCancelMqMessage.class;
        }
    }
    
    

    供应商取消提醒

    @Component
    @Scope("prototype")
    public class CancelToSupplierMqMessage extends AbstractDealWxMqMessage {
    
    
        private RideOrderInfoDao rideOrderInfoDao;
    
        //private WechatTemplateSettingService wechatTemplateSettingService;
    
    
    //这里额外查询逻辑  
        @Autowired
        private StewardInfoService stewardInfoService;
    
    //注入
        @Autowired
        public CancelToSupplierMqMessage(RideOrderLogService rideOrderLogService, WechatPaySettingDao wechatPaySettingDao, WechatTemplateSettingService wechatTemplateSettingService, RideOrderInfoDao rideOrderInfoDao) {
            super(rideOrderLogService, wechatPaySettingDao, wechatTemplateSettingService, rideOrderInfoDao);
            this.rideOrderInfoDao = rideOrderInfoDao;
            //this.wechatTemplateSettingService = wechatTemplateSettingService;
        }
    
    //这个里处理 不需要事前逻辑 不做任何处理
        @Override
        public void befor(WxOrderNoBean wxOrderNoBean) {
            //this class not do any req
            return;
        }
    
    
    //逻辑处理
        @Override
        public void doDeal(WxOrderNoBean wxOrderNoBean) {
    //
    //        if (!super.findTemplateSetting(WxPushTypeEnum.CANCEL_to_supplier.getValue())) {
    //            super.setDo(false);
    //            return;
    //        }
    
    // 同理 获取扩展字段
            Map<String, Object> map = JSON.parseObject(wxOrderNoBean.getExtensionField(), Map.class);
            if (map == null) {
                return;
            }
    
    //查询
            RideOrderInfo validOrderInfoList = rideOrderInfoDao.findByOrderNos("", wxOrderNoBean.getOrderNo());
            if (CollectionUtils.isNotEmpty(validOrderInfoList)) {
                // 逻辑省略 
                WechatMessageUtil.cancelOrderInfoToSupplier(stewardList, validOrderInfoList.get(0));
            }
    
            //not log  不需要记录日志 等下一步 操作
            // or
            // Override after method
            super.setDo(false);
    
        }
    
        @Override
        public int getType() {
            return WxPushTypeEnum.CANCEL_to_supplier.getValue();
        }
      @Override
        public Class<?> getClasz() {
            return CancelToSupplierMqMessage.class;
        }
    }
    
    

    最后调用很简单

    
    	//单例里注入多例 还是单例 对象
        @Autowired
        private List<AbstractDealWxMqMessage> abstractDealWxMqMessage;
    
    		  int type = wxOrderNoBean.getType();
            loggerHelper.infoLog("消费监听消息: mqSendWXMessage " + JSON.toJSONString(wxOrderNoBean));
            Optional<AbstractDealWxMqMessage> first = abstractDealWxMqMessage.stream().filter(x -> x.getType() == type).findFirst();
            if (!first.isPresent()) {
                return;
            }
            // 单例里注入多例 还是单例 对象    
            // 这里是保证线程安全  使用多例 每次从容器获取
            Class<?> classz = first.get().getClasz();
            AbstractDealWxMqMessage beanByClass = (AbstractDealWxMqMessage) SpringContextUtils.getBeanByClass(classz);
            beanByClass.dealMqMessage(wxOrderNoBean);
    
    

    mq 类型bean WxOrderNoBean

    public class WxOrderNoBean {
       private String orderNo;
        /**
         * 1 订单改派
         * 2 出票成功(确认并且填入司机信息)订单改派
         */
        private int type;
        /**
         * 扩展字段  这里我们传json 了 
         */
        private String extensionField;
    
        public WxOrderNoBean() {
        }
    
        public WxOrderNoBean(String orderNo, int type) {
            this.orderNo = orderNo;
            this.type = type;
        }
    
        public WxOrderNoBean(String orderNo, int type, String extensionField) {
            this.orderNo = orderNo;
            this.type = type;
            this.extensionField = extensionField;
        }
    
      //get  set 省略
    
    
    }
    
    

    最后类图奉上

    在这里插入图片描述

    这样看是不是很清晰,也很好扩展。

  • 相关阅读:
    spring复习04,静态代理动态代理,AOP
    Matlab 点云迭代加权最小二乘法(IRLS)
    技术类公司与业务类公司转型科技智能公司的异同
    《HelloGitHub》第 77 期
    运行项目报错 proxy error: could not proxy request...
    注解方式配置SpringMVC
    基于Springboot实现汽车租赁平台管理系统项目【项目源码】计算机毕业设计
    XMLHttpRequest对象的Get请求和Post请求的用法
    java 容器
    洛谷P5545 炸弹攻击2
  • 原文地址:https://blog.csdn.net/weixin_38361347/article/details/127043030