当前位置:网站首页>你還不知道責任鏈模式的使用場景嗎?

你還不知道責任鏈模式的使用場景嗎?

2022-04-23 14:39:00 炒燜煎糖板栗

概述

在代碼中我們經常會有if…else…判斷,一個條件不滿足就進行下一個判斷,這種就類似於責任鏈模式,只不過責任鏈模式是通過對象來過濾。

場景

在物聯網行業中,一個設備會以一定的頻率向服務器推送數據,方便服務器對機器進行一個數據采集和監控,這個數據的類型是多種多樣的。例如娃娃機來說:會有設備狀態的數據、設備定比特的數據、設備報警的數據等等各種數據。每一種類型的數據都由很多個字段組成,例如設備狀態數據包含:當前時間、機器號、機器狀態(上線、下線、離線),一般都是以二進制的形式進行傳輸,為了方便就假設設備以JSON的格式上報過來,我接收到數據再進行一個相應的處理。

image-20220421214953234

普通的代碼實現

首先能想到的就是利用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);
    }

}

運行結果

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

}

image-20220423140135494

  • 如果有新的設備消息類型,只需要加一個新的事件處理類,其他代碼不用變化,這樣符合開放封閉原則還有單一原則,也增加了程序的靈活性。
  • 具體使用到哪個類型也不需要我們自己,交給程序運行時處理
  • 使用Map集合的方式,直接從集合裏面根據特征找到對應的處理器,跟其他博客設置使用下一個處理者進行判斷的方法類似,如果鏈條比較長那麼使用下一個處理者方法不合適,需要從頭遍曆到尾部。
  • 還可以控制請求順序,集合的話通過增加一個排序字段

總結

責任鏈模式其實就是靈活的if..else..語句,將多個處理者連接成一條鏈。 接收到請求後, 它會 “詢問” 每個處理者是否能够對其進行處理。 這樣所有處理者都有機會來處理請求

使用場景

  • 當必須按順序執行多個處理者時,可以使用該模式
  • 如果所需處理者及其順序必須在運行時進行改變, 可以使用責任鏈模式
  • 當程序需要使用不同方式處理不同種類請求,而且請求類型和順序預先未知時,可以使用責任鏈模式

版权声明
本文为[炒燜煎糖板栗]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204231439160668.html