业务背景
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; } }