概述
在代码中我们经常会有if…else…判断,一个条件不满足就进行下一个判断,这种就类似于责任链模式,只不过责任链模式是通过对象来过滤。
场景
在物联网行业中,一个设备会以一定的频率向服务器推送数据,方便服务器对机器进行一个数据采集和监控,这个数据的类型是多种多样的。例如娃娃机来说:会有设备状态的数据、设备定位的数据、设备报警的数据等等各种数据。每一种类型的数据都由很多个字段组成,例如设备状态数据包含:当前时间、机器号、机器状态(上线、下线、离线),一般都是以二进制的形式进行传输,为了方便就假设设备以JSON的格式上报过来,我接收到数据再进行一个相应的处理。
普通的代码实现
首先能想到的就是利用if…else…,如果是设备报警的数据我就使用设备报警处理器处理,超简单的,开始编码~
1、实体类
DeviceAlarm类
package com.ylc.model; import lombok.Data; /** * 设备状态实体类 * @author yanglingcong * @date 2022/4/20 21:08 */ @Data public class DeviceStatus { /** * 更新时间 */ private long updateTime; /** * 状态 * 0 未准备 * 1 准备 * 2 正常运行 * 3 异常 */ private Integer state; /** * 数据类型 */ private String type; }
DeviceGps类
/** * 设备GPS实体类 * * @author yanglingcong * @date 2022/4/20 21:08 */ @Data public class DeviceGps { /** * 经度 */ private Float longitude; /** * 纬度 */ private Float latitude; /** * 水平分量精度因子: */ private Float hdop; }
DeviceAlarm类
package com.ylc.model; import lombok.Data; /** * 设备报警实体类 * * @author yanglingcong * @date 2022/4/20 21:08 */ @Data public class DeviceAlarm { /** * 报警消息 */ private String alarmMsg; /** * 报警状态 */ private Integer alarmStatus; }
2、消息的枚举类型
package com.ylc.model; import lombok.Getter; /** * 设备消息枚举类型 * @author yanglingcong * @date 2022/4/20 21:08 */ @Getter public enum eventEnum { STATUS("10001"), ALARM("10002"), GPS("10003"); private String code; eventEnum(String code){ this.code=code; } }
3、事件接口
/** * 处理器接口 * @author yanglingcong * @date 2022/4/19 22:59 */ public interface AbstractHandler { String getEventType(); void handle(JSONObject jsonObject); }
3、事件处理
DeviceAlarmEvent
/** * 设备报警事件 * @author yanglingcong * @date 2022/4/19 22:59 */ @Slf4j @Component public class DeviceAlarmEvent implements AbstractHandler{ @Override public String getEventType() { return eventEnum.ALARM.getCode(); } @Override public void handle(JSONObject jsonObject) { DeviceAlarm deviceAlarm = jsonObject.toJavaObject(DeviceAlarm.class); log.info("设备报警事件被处理"); //业务处理..... } }
DeviceGpsEvent
/** * 设备定位事件 * @author yanglingcong * @date 2022/4/19 22:59 */ @Component @Slf4j public class DeviceGpsEvent implements AbstractHandler{ @Override public String getEventType() { return eventEnum.GPS.getCode(); } @Override public void handle(JSONObject jsonObject) { DeviceGps deviceGps = jsonObject.toJavaObject(DeviceGps.class); //业务处理..... log.info("设备定位事件被处理"); } }
DeviceStatusEvent
/** * 设备状态事件 * @author yanglingcong * @date 2022/4/19 22:59 */ @Slf4j @Component public class DeviceStatusEvent implements AbstractHandler{ @Override public String getEventType() { return eventEnum.STATUS.getCode(); } @Override public void handle(JSONObject jsonObject){ DeviceStatus deviceStatus = jsonObject.toJavaObject(DeviceStatus.class); //业务处理..... log.info("设备状态事件被处理"); } }
4、消息分发中心
package com.ylc.handle; import com.alibaba.fastjson.JSONObject; import com.ylc.model.eventEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * 数据事件处理类 * @author yanglingcong */ @Slf4j @Component public class PushEvent { /** * 数据分发到对应的事件处理 */ public void dispatch(JSONObject jsonObject){ String code = (String) jsonObject.get("type"); //如果是设备状态数据 if(code.equals(eventEnum.STATUS.getCode())){ log.info("开始处理设备状态数据"); DeviceStatusEvent statusEvent=new DeviceStatusEvent(); statusEvent.handle(jsonObject); } //如果是设备定位数据 else if(code.equals(eventEnum.GPS.getCode())){ log.info("开始处理设备定位数据"); DeviceGpsEvent deviceGpsEvent=new DeviceGpsEvent(); deviceGpsEvent.handle(jsonObject); } //如果是设备报警数据 else if(code.equals(eventEnum.ALARM.getCode())){ log.info("开始处理设备定位数据"); DeviceStatusEvent statusEvent=new DeviceStatusEvent(); statusEvent.handle(jsonObject); } } }
6、测试
@Slf4j public class MessageHandleTest { @Test public void testDeviceStatus(){ DeviceStatus deviceStatus=new DeviceStatus(); deviceStatus.setType(eventEnum.STATUS.getCode()); deviceStatus.setUpdateTime(1653532367); deviceStatus.setState(1); JSONObject jsonObject= JSON.parseObject(JSONObject.toJSONString(deviceStatus)); PushEvent pushEvent=new PushEvent(); log.info("开始分发消息:{}",deviceStatus.toString()); pushEvent.dispatch(jsonObject); } }
运行结果
但是这样会有很多问题,如果还有其他类型的数据那么又要增加判断,这个条件判定的顺序也是写死的,非常不灵活,接下来用责任链模式进行优化
责任链实现
1、实体类 略
2、事件处理 略
3、消息分发中心
package com.ylc.handle; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import lombok.var; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * 数据事件处理类 * @author yanglingcong */ @Slf4j @Component public class PushEvent implements ApplicationContextAware { /** * 实现类集合 * */ private Map<String, List<AbstractHandler>> routerMap; @Autowired ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.routerMap =applicationContext.getBeansOfType(AbstractHandler.class).values() .stream().collect(Collectors.groupingBy(AbstractHandler::getEventType)); } /** * 数据分发到对应的事件处理 */ public void dispatch(JSONObject jsonObject){ String code = (String) jsonObject.get("type"); List<AbstractHandler> pushEventHandlers= this.routerMap.get(code); for (AbstractHandler pushEventHandler : pushEventHandlers) { log.info("开始处理{}事件",pushEventHandler.getEventType()); pushEventHandler.handle(jsonObject); } } }
4、测试
package com.ylc; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.ylc.handle.AbstractHandler; import com.ylc.handle.PushEvent; import com.ylc.model.DeviceStatus; import com.ylc.model.eventEnum; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class MessageHandleTest { @Autowired PushEvent pushEvent; @Test public void testDeviceStatus(){ DeviceStatus deviceStatus=new DeviceStatus(); deviceStatus.setType(eventEnum.STATUS.getCode()); deviceStatus.setUpdateTime(1653532367); deviceStatus.setState(1); JSONObject jsonObject= JSON.parseObject(JSONObject.toJSONString(deviceStatus)); log.info("开始分发消息:{}",deviceStatus.toString()); pushEvent.dispatch(jsonObject); } }
- 如果有新的设备消息类型,只需要加一个新的事件处理类,其他代码不用变化,这样符合开放封闭原则还有单一原则,也增加了程序的灵活性。
- 具体使用到哪个类型也不需要我们自己,交给程序运行时处理
- 使用Map集合的方式,直接从集合里面根据特征找到对应的处理器,跟其他博客设置使用下一个处理者进行判断的方法类似,如果链条比较长那么使用下一个处理者方法不合适,需要从头遍历到尾部。
- 还可以控制请求顺序,集合的话通过增加一个排序字段
总结
责任链模式其实就是灵活的if..else..语句,将多个处理者连接成一条链。 接收到请求后, 它会 “询问” 每个处理者是否能够对其进行处理。 这样所有处理者都有机会来处理请求
使用场景
- 当必须按顺序执行多个处理者时,可以使用该模式
- 如果所需处理者及其顺序必须在运行时进行改变, 可以使用责任链模式
- 当程序需要使用不同方式处理不同种类请求,而且请求类型和顺序预先未知时,可以使用责任链模式