你还不知道责任链模式的使用场景吗?

博客 分享
0 212
张三
张三 2022-04-23 14:58:21
悬赏:0 积分 收藏

你还不知道责任链模式的使用场景吗?

概述

在代码中我们经常会有if…else…判断,一个条件不满足就进行下一个判断,这种就类似于责任链模式,只不过责任链模式是通过对象来过滤。

场景

在物联网行业中,一个设备会以一定的频率向服务器推送数据,方便服务器对机器进行一个数据采集和监控,这个数据的类型是多种多样的。例如娃娃机来说:会有设备状态的数据、设备定位的数据、设备报警的数据等等各种数据。每一种类型的数据都由很多个字段组成,例如设备状态数据包含:当前时间、机器号、机器状态(上线、下线、离线),一般都是以二进制的形式进行传输,为了方便就假设设备以JSON的格式上报过来,我接收到数据再进行一个相应的处理。

image-20220421214953234

普通的代码实现

首先能想到的就是利用if…else…,如果是设备报警的数据我就使用设备报警处理器处理,超简单的,开始编码~

1、实体类

DeviceAlarm类

package com.ylc.model;import lombok.Data;/** * 设备状态实体类 * @author yanglingcong * @date 2022/4/20 21:08 */@Datapublic 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 */@Datapublic 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 */@Datapublic class DeviceAlarm {    /**     * 报警消息     */    private String alarmMsg;    /**     * 报警状态     */    private Integer alarmStatus;}

2、消息的枚举类型

package com.ylc.model;import lombok.Getter;/** * 设备消息枚举类型 * @author yanglingcong * @date 2022/4/20 21:08 */@Getterpublic 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@Componentpublic 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@Slf4jpublic 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@Componentpublic 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@Componentpublic 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、测试

@Slf4jpublic 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);    }}

运行结果

image-20220421221949165

但是这样会有很多问题,如果还有其他类型的数据那么又要增加判断,这个条件判定的顺序也是写死的,非常不灵活,接下来用责任链模式进行优化

责任链实现

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@Componentpublic 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)@SpringBootTestpublic 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);    }}

image-20220423140135494

  • 如果有新的设备消息类型,只需要加一个新的事件处理类,其他代码不用变化,这样符合开放封闭原则还有单一原则,也增加了程序的灵活性。
  • 具体使用到哪个类型也不需要我们自己,交给程序运行时处理
  • 使用Map集合的方式,直接从集合里面根据特征找到对应的处理器,跟其他博客设置使用下一个处理者进行判断的方法类似,如果链条比较长那么使用下一个处理者方法不合适,需要从头遍历到尾部。
  • 还可以控制请求顺序,集合的话通过增加一个排序字段

总结

责任链模式其实就是灵活的if..else..语句,将多个处理者连接成一条链。 接收到请求后, 它会 “询问” 每个处理者是否能够对其进行处理。 这样所有处理者都有机会来处理请求

使用场景

  • 当必须按顺序执行多个处理者时,可以使用该模式
  • 如果所需处理者及其顺序必须在运行时进行改变, 可以使用责任链模式
  • 当程序需要使用不同方式处理不同种类请求,而且请求类型和顺序预先未知时,可以使用责任链模式
posted @ 2022-04-23 14:29 炒焖煎糖板栗 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员