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

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

概述

在代码中我们经常会有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..语句,将多个处理者连接成一条链。 接收到请求后, 它会 “询问” 每个处理者是否能够对其进行处理。 这样所有处理者都有机会来处理请求

使用场景

  • 当必须按顺序执行多个处理者时,可以使用该模式
  • 如果所需处理者及其顺序必须在运行时进行改变, 可以使用责任链模式
  • 当程序需要使用不同方式处理不同种类请求,而且请求类型和顺序预先未知时,可以使用责任链模式
相关文章
|
6月前
|
设计模式 搜索推荐 数据库连接
第二篇 创建型设计模式 - 灵活、解耦的创建机制
第二篇 创建型设计模式 - 灵活、解耦的创建机制
|
3月前
|
设计模式 存储 开发者
如何在业务代码中优雅地使用责任链模式
责任链模式是一种行为设计模式,本文从责任链模式的定义到其优雅之处、合适的应用场景、应用示例、实现步骤等方面详细讲述了如何在业务代码中优雅的使用责任链模式。
|
3月前
|
设计模式 Java 开发者
装饰器模式和观察者模式的区别
【8月更文挑战第24天】
29 0
|
3月前
|
设计模式 Java 数据安全/隐私保护
装饰器模式与观察者模式的区别
【8月更文挑战第24天】
27 0
|
5月前
|
设计模式 监控 开发者
在业务代码中如何优雅地使用责任链模式
在日常开发中,随着业务逻辑的复杂性和系统规模的增加,我们开发者往往面临着代码结构混乱、模块间耦合度高等问题,如何保持代码的清晰、可维护和可扩展性成为了一个重要的挑战。为了解决这个问题,我们需要借助一些设计模式来优化代码结构,提高代码的可读性和可维护性,那么责任链模式就是一种非常实用的设计模式,是解决这一问题的有效手段之一,它可以帮助我们优雅地处理业务逻辑中的复杂请求,该模式通过构建一系列处理者对象,并将它们连接成一条链,使得请求可以在这些处理者之间传递,直到被恰当处理。那么接下来,我们将探讨如何在业务代码中优雅地使用责任链模式,欢迎在评论区留言交流。
74 5
在业务代码中如何优雅地使用责任链模式
|
5月前
|
设计模式 算法 数据处理
|
6月前
|
设计模式 缓存 Java
如何在业务代码中优雅地使用责任链模式?
【5月更文挑战第26天】责任链模式(Chain of Responsibility Pattern)是一种设计模式,用于处理请求的发送者和接收者之间的解耦。
111 1
|
6月前
|
设计模式 安全 Java
Java设计模式—单例模式的实现方式和使用场景
那么为什么要有单例模式呢?这是因为有的对象的创建和销毁开销比较大,比如数据库的连接对象。所以我们就可以使用单例模式来对这些对象进行复用,从而避免频繁创建对象而造成大量的资源开销。
160 1
|
设计模式 Java 应用服务中间件
【设计模式——学习笔记】23种设计模式——职责链/责任链模式(Chain of Responsibility)(原理讲解+应用场景介绍+案例介绍+Java代码实现)
【设计模式——学习笔记】23种设计模式——职责链/责任链模式(Chain of Responsibility)(原理讲解+应用场景介绍+案例介绍+Java代码实现)
144 0
|
设计模式 算法 Java
灵活选择:解析Java设计模式中的策略模式
在软件开发领域,设计模式是一组经过验证的最佳实践方法,用于解决各种常见问题。策略模式是一种行为型设计模式,其目标是定义一系列的算法,将它们封装成独立的策略类,并且使得这些策略类可以相互替换。在本文中,我们将深入了解策略模式的核心思想、应用场景以及它在Java中的实际运用。
101 0