• 设计模式(策略、工厂、模板、责任链)的使用实例


    业务背景

    server与单片机交互使用私有16进制协议,其中某个字节代表该帧的命令类型(cmd_type)。

    比如 0xaa 0xff 0x01........      0xaa 0xff 0x02.......    0xaa 0xff 0x03 ......

    0x01表示上报心跳。 0x02表示上报时间。 0x03表示上报水温等

    server需要针对不同的帧做不同的处理,然后给单片机对应的响应

    原先代码结构是在一个类中使用 switch (cmd_type)   case 0x01. ..... ;  case 0x02 ....... case 0x03 .....

    依赖错综复杂,代码臃肿混乱, 且每次添加新的命令都需要扩写该类添加其复杂性,不满足开闭原则。 

    改写


    首先抽象一个标志接口 MachineReportHandler

    public interface MachineReportHandler {
        
        void handle(FrameResponse response, WHFrame frame);
    }

    然后基于处理器都是先处理然后响应的过程使用模板方法抽象  AbstractMachineReportHandler

    public abstract class AbstractMachineReportHandler implements MachineReportHandler {
        
        @Override
        public void handle(FrameResponse response, WHFrame frame) {
            try {
                doHandleLogic(frame);
                
            } finally {
                
                doResponse(response, frame);
            }
        }
    
        protected abstract void doHandleLogic(WHFrame frame);
    
        protected abstract void doResponse(FrameResponse response, WHFrame frame);
    }

    具体某个命令的处理器继承AbstractMachineReportHandler重写doHandleLogic和doResponse方法即可,下面列出一个例子

    @Component("HotMachineRequestHandler_cmd_01")
    public class HotMachineRegHandler extends AbstractMachineReportHandler {
        
        private Logger logger = Logger.getLogger(HotMachineRegHandler.class);
        
        @Autowired
        MachineRegisterService machineRegisterService;
        
        @Autowired
        MachineMapper machineMapper;
        
        @Autowired
        MachineDao machineDao;
        
        @Autowired
        SimDao simDao;
    
        @Override
        protected void doHandleLogic(WHFrame frame) {
            logger.info("注册帧:" + frame.toString());
            
            //省略具体业务代码
        }
    
        @Override
        protected void doResponse(FrameResponse res, WHFrame frame) {
           //省略具体业务代码
        } 
    }

    注意这里给bean的命令,前缀统一,后缀使用对应被处理帧命令的具体值,方便工厂中根据帧内容获取对应的处理器

    public class HotMachineReportHandlerFactory {
        
        private static Logger logger = Logger.getLogger(HotMachineReportHandlerFactory.class);
    
        private static final String BEAN_NAME_PREFIX = "HotMachineRequestHandler_cmd_";
        
        public static MachineReportHandler getHandler(byte cmd_type) {
            
            String bean_name_suffix = StringUtil.padLeft(Integer.toHexString(cmd_type & 0xff), 2, '0');
            
            String beanName = BEAN_NAME_PREFIX + bean_name_suffix;
            
            MachineReportHandler bean = null;try {
                bean = (MachineReportHandler) SpringContextUtil.getBean(beanName);
            } catch (BeansException err) {
                logger.error("未找到开水器上报命令类型为cmd_type: 0x" + bean_name_suffix + "对应的处理器");
                bean = null;
            }
            
            return bean;
        }
        
    }

    最后改写大块switch case语句

      MachineReportHandler bean = HotMachineReportHandlerFactory.getHandler(whFrame.getFrame_cmd());
      if (null != bean) {
            bean.handle(this, whFrame);
      }

    后期刷卡打水命令需求变更, 刷卡打水命令需要根据设备的类型配置,采取不同的处理方式。  普通设备保持原业务逻辑, 加卡机将卡号入库,清卡机将卡号删除。

    采用责任链模式扩展,设置好链中处理器的顺序,每个处理器先判断是否由自己处理,是则处理,否则交给下一个处理器

    payload_res = AbstractReportCardnoHandler.getChain().prehandle(mac, cardno);
    public  abstract class AbstractReportCardnoHandler implements ReportCardnoHandler {
        
        protected AbstractReportCardnoHandler nextHandler;
        
        
        public static AbstractReportCardnoHandler getChain () {
            AbstractReportCardnoHandler add_card_handler = SpringContextUtil.getBean(AddCardMachineReportCardnoHandler.class);
            AbstractReportCardnoHandler clear_card_handler = SpringContextUtil.getBean(ClearCardMachineReportCardnoHandler.class);
            AbstractReportCardnoHandler zju_handler = SpringContextUtil.getBean(ZJUMachineReportCardnoHandler.class);
            AbstractReportCardnoHandler normal_handler = SpringContextUtil.getBean(NormalMachineReportCardnoHandler.class);
            
            add_card_handler.setNextHandler(clear_card_handler);
            clear_card_handler.setNextHandler(zju_handler);
            zju_handler.setNextHandler(normal_handler);
            
            return add_card_handler;
        }
        
        public byte[] prehandle(String mac, String cardno) {
            if (isDueToHandle(mac, cardno)) {
                return handle(mac, cardno);
            }
            
            return getNextHandler().prehandle(mac, cardno);
            
        }
        
        protected abstract boolean isDueToHandle(String mac, String cardno);
    
        public abstract byte[] handle(String mac, String cardno);
    
    
        public AbstractReportCardnoHandler getNextHandler() {
            return nextHandler;
        }
    
        public void setNextHandler(AbstractReportCardnoHandler nextHandler) {
            this.nextHandler = nextHandler;
        }
        
        
    
    }
  • 相关阅读:
    自动化测试-18.selenium之bugFree代码注释
    自动化测试-16.selenium数据的分离之Excel的使用
    自动化测试-15.selenium单选框与复选框状态判断
    自动化测试-14.selenium加载FireFox配置
    自动化测试-13.selenium执行JS处理滚动条
    Lucas-Kanade算法总结
    迟来的2013年总结及算法工程师/研究员找工作总结
    Android从文件读取图像显示的效率问题
    Viola Jones Face Detector
    谈谈Android中的SurfaceTexture
  • 原文地址:https://www.cnblogs.com/xdxy/p/9949371.html
Copyright © 2020-2023  润新知