不讲故事的设计模式-责任链模式

news2024/11/17 17:30:36

文章目录

  • 基本概念
  • 责任链模式标准结构
  • 责任链模式的扩展
    • 仿照Servlet Filter的实现方式
  • 责任链模式的应用场景
    • 业务场景
    • 开源框架中的应用
  • 责任链模式的缺点
  • 关于设计模式乱用的现象

基本概念

在责任链模式中可以定义多个处理节点(Handler),当接收到客户端请求之后,该请求会依次经过每个处理节点,直到某个节点终止将它传递,或者所有节点都处理完为止。

这样设计的优点在于可以有效避免请求发送者与请求接收者之间的耦合关系,符合开闭原则,提高代码的扩展性。

责任链模式标准结构

image.png

角色作用
Handler定义一个处理请求的接口,主要包含抽象处理方法和后续Handler
Concrete HandlerHandler的具体实现者,主要用于负责业务逻辑处理,也可以控制对后继Handler的传递行为

以下是一种按照责任链模式处理方式。

public abstract class Handler {

    private Handler successor;

    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    public Handler getSuccessor() {
        return successor;
    }

    public abstract void handleRequest(String msg);

}
public class ConcreteHandlerA extends Handler {

    @Override
    public void handleRequest(String msg) {
        if (Objects.nonNull(msg) && !msg.isEmpty()) {
            System.out.println("ConcreteHandlerA pass");
            if (getSuccessor() != null) {
                getSuccessor().handleRequest(msg);
            }
        } else {
            System.out.println("msg 不能为空!");
        }
    }
}

public class ConcreteHandlerB extends Handler {

    @Override
    public void handleRequest(String msg) {
        if (msg.length() <= 16) {
            System.out.println("ConcreteHandlerB pass");
            if (getSuccessor() != null) {
                getSuccessor().handleRequest(msg);
            }
        } else {
            System.out.println("msg长度不能超过16!");
        }
    }
}


public class Client {
    public static void main(String[] args) {
        Handler handler1 = new ConcreteHandlerA();
        Handler handler2 = new ConcreteHandlerB();
        handler1.setSuccessor(handler2);
        handler1.handleRequest("text");
    }
}

UML类图
image.png

责任链模式的扩展

很明显,上面的这种代码实现方式实在是不够优雅,每个具体的处理器类还得要处理对下一个处理器的调用,Client
也必须得熟悉每个处理器类之间的调用关系、顺序等,这些条件都很容易导致代码出现BUG

我们可以改成下面这种实现方式,是让每个处理器都能处理到请求,且能自己进行判定是否需要处理,不存在被某个处理器终止而不继续向后传递的情况。

public interface IHandler {

    void handleRequest(FilterRequestDTO filterRequestDTO);

    default boolean accept(FilterRequestDTO filterRequestDTO) {
        return true;
    }
    
}
public class HandlerChain {

    private List<IHandler> handlers = new ArrayList<>();

    public void addHandler(IHandler handler) {
        this.handlers.add(handler);
    }

    public void handle(FilterRequestDTO filterRequestDTO) {
        for (IHandler handler : handlers) {
            if (!handler.accept(filterRequestDTO)) {
                // handler不处理
                continue;
            }
            handler.handleRequest(filterRequestDTO);
        }
    }

}
public class ConcreteHandler1 implements IHandler {
    @Override
    public void handleRequest(FilterRequestDTO filterRequestDTO) {
        System.out.println("ConcreteHandler1 handleRequest");
    }

    @Override
    public boolean accept(FilterRequestDTO filterRequestDTO) {
        String msg = filterRequestDTO.getMsg();
        return Objects.nonNull(msg) && !msg.isEmpty();
    }

}


public class ConcreteHandler2 implements IHandler {
    @Override
    public void handleRequest(FilterRequestDTO filterRequestDTO) {
        System.out.println("ConcreteHandler2 handleRequest");
    }

    @Override
    public boolean accept(FilterRequestDTO filterRequestDTO) {
        String msg = filterRequestDTO.getMsg();
        return msg.length() >= 16;
    }

}

public class Client {
    public static void main(String[] args) {
        HandlerChain chain = new HandlerChain();
        chain.addHandler(new ConcreteHandler1());
        chain.addHandler(new ConcreteHandler2());
        FilterRequestDTO filterRequestDTO = new FilterRequestDTO();
        filterRequestDTO.setMsg("hello HandlerChain");
        chain.handle(filterRequestDTO);
    }
}

除此之外,我们还可以将continue改为break即可变为可终止责任链向下传递行为的方式。

public class HandlerChain {

    private List<IHandler> handlers = new ArrayList<>();

    public void addHandler(IHandler handler) {
        this.handlers.add(handler);
    }

    public void handle(FilterRequestDTO filterRequestDTO) {
        for (IHandler handler : handlers) {
            if (!handler.accept(filterRequestDTO)) {
                // handler不处理
                break;
            }
            handler.handleRequest(filterRequestDTO);
        }
    }

}

仿照Servlet Filter的实现方式

Java Servlet规范中定义的Filter组件,就是一个责任链模式的实际运用场景,Filter职责可以包含鉴权、参数校验、限流、写日志等。

image.png

public interface Filter {

    default boolean accept(FilterRequestDTO filterRequestDTO) {
        return true;
    }

    void doFilter(FilterRequestDTO filterRequestDTO, FilterChain filterChain);

}
public interface FilterChain {

    void doFilter(FilterRequestDTO filterRequestDTO);

}
public class BasePriceFilter implements Filter {
    @Override
    public boolean accept(FilterRequestDTO filterRequestDTO) {
        return true;
    }

    @Override
    public void doFilter(FilterRequestDTO filterRequestDTO, FilterChain filterChain) {
        if (accept(filterRequestDTO)) {
            System.out.println("base price 业务逻辑处理");
        }
        filterChain.doFilter(filterRequestDTO);
    }

    public static BasePriceFilter create() {
        return new BasePriceFilter();
    }
}
public class DiscountPriceFilter implements Filter {
    @Override
    public boolean accept(FilterRequestDTO filterRequestDTO) {
        return true;
    }

    @Override
    public void doFilter(FilterRequestDTO filterRequestDTO, FilterChain filterChain) {
        if (accept(filterRequestDTO)) {
            System.out.println("discount price 业务逻辑处理");
        }
        filterChain.doFilter(filterRequestDTO);
    }

    public static DiscountPriceFilter create() {
        return new DiscountPriceFilter();
    }
}
public final class PriceFilterChain implements FilterChain {

    private List<Filter> filters;

    private int filterSize;

    private int pos = 0;

    @Override
    public void doFilter(FilterRequestDTO filterRequestDTO) {
        if (pos < filterSize) {
            Filter filter = filters.get(pos++);
            filter.doFilter(filterRequestDTO, this);
        } else {
            System.out.println("处理完了");
        }
    }

    public PriceFilterChain(List<Filter> filters) {
        this.filters = filters;
        this.filterSize = filters.size();
    }
}
public class FilterChainManager {

    private FilterChain filterChain;

    private void init() {
        List<Filter> filters = new ArrayList<>();
        filters.add(BasePriceFilter.create());
        filters.add(DiscountPriceFilter.create());
        this.filterChain = new PriceFilterChain(filters);
    }

    public void process(FilterRequestDTO filterRequestDTO) {
        filterChain.doFilter(filterRequestDTO);
    }

    public static void main(String[] args) {
        FilterChainManager filterChainManager = new FilterChainManager();
        filterChainManager.init();
        filterChainManager.process(new FilterRequestDTO());
    }

}

在这里插入图片描述

责任链模式的应用场景

业务场景

商品报价

通常一个商品在整个报价链路中会涉及到多种价格的计算,包括:基础价格、商家报价、平台报价、折扣价等,这其中每一种价格都有自己的计算逻辑,且有些价格是需要在前一个价格的基础上叠加计算,最终得出消费者到手价,所以可以运用责任链模式来处理。

开源框架中的应用

Spring Interceptor

除了前面提到的Servlet Filter之外,类似的还有Spring Interceptor

HandlerInterceptor相当于Handler.

public interface HandlerInterceptor {

	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return true;
	}

	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}

	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}

}

HandlerExecutionChain接口相当于HandlerChain

public class HandlerExecutionChain {

	private final Object handler;
	private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
	private int interceptorIndex = -1;

	public void addInterceptor(HandlerInterceptor interceptor) {
		this.interceptorList.add(interceptor);
	}
	
	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		for (int i = 0; i < this.interceptorList.size(); i++) {
			HandlerInterceptor interceptor = this.interceptorList.get(i);
			if (!interceptor.preHandle(request, response, this.handler)) {
				triggerAfterCompletion(request, response, null);
				return false;
			}
			this.interceptorIndex = i;
		}
		return true;
	}

	void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
			throws Exception {
		for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
			HandlerInterceptor interceptor = this.interceptorList.get(i);
			interceptor.postHandle(request, response, this.handler, mv);
		}
	}

	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
		for (int i = this.interceptorIndex; i >= 0; i--) {
			HandlerInterceptor interceptor = this.interceptorList.get(i);
			try {
				interceptor.afterCompletion(request, response, this.handler, ex);
			}
			catch (Throwable ex2) {
				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
			}
		}
	}
}


责任链模式的缺点

  1. 在实际业务应用中,责任链模式由于是串行执行的。每个请求都需要依次经过每个处理节点,直到找到能够处理的节点或者到达链路的末尾。在这个过程中,每个节点都会进行一定的处理操作,如RPC请求、数据库操作等,这些操作会消耗一定的时间和资源。如果链路比较长,那么这些消耗就会累积起来,导致整个系统的响应时间变长,性能下降。
  2. 责任链模式一般需要客户端了解并保证链路上每个处理节点执行逻辑的合理性,因此从整体上看,会增加一定的复杂性,每个处理节点都有自己特定的处理逻辑和条件判断,客户端需要了解这些逻辑并保证正确地设置节点之间的顺序和依赖关系。如果节点设置错误或者逻辑不合理,可能会导致请求无法得到正确处理或者出现意外的结果。

对比直接用分支条件语句

下面这个小案例也可以用来简单说明。

public class Main {
    
    public static void main(String[] args) {
        String msg = "hello HandlerChain";

        if (Objects.nonNull(msg) && !msg.isEmpty()) {
            System.out.println("chain 1");
        }
        if (msg.length() >= 16) {
            System.out.println("chain 2");
        }
        
    }
}

如果像上面这样直接用if语句,代码看起来明显更加简洁直观,反观责任链模式将每个条件分支的处理逻辑封装在独立的处理节点中,反而增加了系统的复杂性和理解难度,特别是当链路比较长或者处理逻辑比较复杂时。

因此,是否使用责任链模式取决于具体的情况和需求。如果只需要处理少量的条件分支,并且逻辑也比较简单,则使用if语句可能更加合适。

当然,运用设计模式主要是为了解决代码的复用性、扩展性等问题,虽然直接使用if方式看起来简单了很多,但却不能满足开闭原则,更重要的是不能将其运用在框架中让使用者可以直接进行扩展。

关于设计模式乱用的现象

最后,再来聊聊关于设计模式乱用的问题,主要突出为以下两个阶段:

  1. 新手:这经常发生在刚接触设计模式不久的阶段,急于找地方使用的情况,开发人员不考虑实际的业务场景,完全是为了用设计模式而用设计模式,甚至是先想好要用什么样的设计模式,然后让业务逻辑尽量往这个模式上去套。
  2. 胜任者:过了新手阶段之后,此时你对设计模式也有一定使用经验了,开始意识到胡乱使用设计模式造成的问题了,懂得了理解业务场景才是关键,那还有什么问题呢?此时的阶段就好比术和道的区别,术是多变的,就像我们常说的23种设计模式一样,而道是不变的,无论哪种设计模式始终都是以几种设计原则为依据,正所谓万变不离其宗,设计模式的使用不应当局限于形式上,要能灵活变换。
  3. 精通者:如果跨过新手阶段的关键在于多写多练的话,那么要跨过胜任者阶段则要多思考了,得道的关键在于领悟。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1063208.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【从0开始配置前后端项目】——Docker环境配置

1. 准备一台纯净的服务器 镜像&#xff1a;CentOS 7.9 64位 CPU & 内存&#xff1a;2核2G 系统盘&#xff1a;60GB 峰值带宽&#xff1a;30Mbps 流量包&#xff1a;600GB / 600GB 2. 安装Docker 2.1 卸载旧的版本 $ sudo yum remove docker \docker-client \docker-cl…

芯片不是st公司,cmsis-dap调试器的使用

存在的问题&#xff1a; 分析&#xff1a;因为这块板子不是我们自己画的&#xff0c;也没细看芯片上的丝印&#xff0c;一开始我还以为芯片是盗版的&#xff0c;然后有人看到了丝印的前缀是GD&#xff0c;我们就意识到可能是芯片包没装对的问题了解决方法&#xff1a; &#xf…

互联网Java工程师面试题·Memcached篇·第一弹

目录 1、Memcached 是什么&#xff0c;有什么作用&#xff1f; 1.1 memcached 服务在企业集群架构中有哪些应用场景&#xff1f; 1.1.1 作为数据库的前端缓存应用 1.1.2 作业集群的 session 会话共享存储 2、Memcached 服务分布式集群如何实现&#xff1f; 3、Memcach…

【LeetCode力扣】LCR170 使用归并排序的思想解决逆序对问题(详细图解)

目录 1、题目介绍 2、解题思路 2.1、暴力破解法 2.2、归并排序思想 2.2.1、画图详细讲解 2.2.2、归并排序解决逆序对的代码实现 1、题目介绍 首先阅读题目可以得出要点&#xff0c;即当前数大于后数时则当作一个【逆序对】&#xff0c;而题目是要求在一个数组中计算一共存…

专业综合课程设计 - 优阅书城项目(第一版)

此项目是《专业综合课程设计》带练项目 实现的功能有&#xff1a; 登录、注销、添加图书、删除图书、编辑图书 包含资源&#xff1a; 优阅书城&#xff08;bookstore&#xff09;源码 数据库数据 课程笔记 下载链接&#xff1a;https://wwpv.lanzoue.com/i79nx1av4doj 登录功…

小谈设计模式(20)—组合模式

小谈设计模式&#xff08;20&#xff09;—组合模式 专栏介绍专栏地址专栏介绍 组合模式对象类型叶节点组合节点 核心思想应用场景123 结构图结构图分析 Java语言实现首先&#xff0c;我们需要定义一个抽象的组件类 Component&#xff0c;它包含了组合节点和叶节点的公共操作&a…

两数之和(Hash表)[简单]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给定一个整数数组nums和一个整数目标值target&#xff0c;请你在该数组中找出"和"为目标值target的那两个整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元…

lv7 嵌入式开发-网络编程开发 12 IP协议与ethernet协议

目录 1 IP协议作用和意义 2 IP数据报首部格式 3 IP数据报分片 4 以太网协议作用和意义&#xff08;链路层&#xff09; 5 练习 1 IP协议作用和意义 IP网的意义 当互联网上的主机进行通信时&#xff0c;就好像在一个网络上通信一样&#xff0c;看不见互连的各具体的网络异…

Vue中如何进行分布式搜索与全文搜索(如Elasticsearch)

在Vue中实现分布式搜索与全文搜索&#xff08;使用Elasticsearch&#xff09; 分布式搜索和全文搜索在现代应用程序中变得越来越重要&#xff0c;因为它们可以帮助用户快速查找和检索大量数据。Elasticsearch是一种强大的分布式搜索引擎&#xff0c;它可以用于实现高性能的全文…

文举论金:非农到来!黄金原油全面走势分析策略独家指导

市场没有绝对&#xff0c;涨跌没有定势&#xff0c;所以&#xff0c;对市场行情的涨跌平衡判断就是你的制胜法宝。欲望&#xff01;有句意大利谚语&#xff1a;让金钱成为我们忠心耿耿的仆人&#xff0c;否则&#xff0c;它就会成为一个专横跋扈的主人。空头&#xff0c;多头都…

IEEE802系列协议知识点总结

IEEE 802 协议包含了以下多种子协议。把这些协议汇集在一起就叫IEEE 802 协议集。 (1)IEEE802.1 IEEE 802.1协议提供高层标准的框架&#xff0c;包括端到端协议、网络互连、网络管理、路由选择、桥接和性能测量。 •IEEE 802.1d:生成树协议(Spanning Tree Protocol&#xff0c…

1500*C. Journey(dfs树的遍历数学期望)

解析&#xff1a; dfs遍历树&#xff0c;到达叶结点时累计答案期望值即可。 #include<bits/stdc.h> using namespace std; #define int long long const int N2e55; int n; double res; vector<int>e[N]; void dfs(int u,int deep,double p,int fa){int t;if(u1) …

【雷达原理】雷达测距原理及实现方法

目录 一、雷达测距原理1.1 基本原理1.2 实现方法1.3 与距离有关的概念 二、MATLAB仿真实验2.1 应用案例2.2 MATLAB代码 一、雷达测距原理 1.1 基本原理 我们知道&#xff0c;电磁波的传播速度为光速 c c c&#xff0c;若雷达与目标之间的距离为 R R R&#xff0c;则雷达发出…

网络爬虫中的代理技术:socks5代理和HTTP代理

网络爬虫是一种非常重要的数据采集工具&#xff0c;但是在进行网络爬虫时&#xff0c;我们经常会遇到一些限制&#xff0c;比如IP封锁、反爬虫机制等&#xff0c;这些限制会影响我们的数据采集效果。为了解决这些问题&#xff0c;我们可以使用代理服务器&#xff0c;其中socks5…

Vue中如何进行自定义图表与可视化图形设计

Vue中如何进行自定义图表与可视化图形设计 在现代Web应用程序开发中&#xff0c;数据可视化图表和图形设计是至关重要的一部分。Vue.js是一个流行的JavaScript框架&#xff0c;它提供了强大的工具来构建交互性强大的用户界面。本文将探讨如何在Vue.js中进行自定义图表和可视化…

好奇喵 | Tor浏览器——访问.onion网址,揭开Dark Web的神秘面纱

前言 在之前的博客中&#xff1a; 1.Surface Web —&#xff1e; Deep Web —&#xff1e; Dark Web&#xff0c;我们解释了表层网络、深层网络等的相关概念&#xff1b; 2.Tor浏览器——层层剥开洋葱&#xff0c;我们阐述了Tor的历史和基本工作原理&#xff1b; 3.Tor浏览器…

Oracle Database Express Edition (XE)配置与部署

获取下载安装包 https://www.oracle.com/cn/database/technologies/xe-downloads.htmlhttps://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/index.html安装.rpm安装包 cd /usr/local/src wget https://download.oracle.com/otn-pub/otn_software/db-express/oracle-d…

实时人脸五观检测:基于libfacedetection(CNN模型)

一、前言 随着人工智能技术的不断发展,人脸检测已成为计算机视觉领域的重要应用之一。人脸检测是一种将输入图像中的人脸位置和轮廓提取出来的技术,广泛应用于人脸识别、智能监控、人机交互等领域。利用libfacedetection开源的人脸检测库,实现人脸检测。 libfacedetection…

华为云云耀云服务器L实例评测|SpringCloud相关组件——nacos和sentinel的安装和配置 运行内存情况 服务器被非法登陆尝试的解决

前言 最近华为云云耀云服务器L实例上新&#xff0c;也搞了一台来玩&#xff0c;期间遇到各种问题&#xff0c;在解决问题的过程中学到不少和运维相关的知识。 本篇博客介绍SpringCloud相关组件——nacos和sentinel的安装和配置&#xff0c;并分析了运行内存情况&#xff0c;此…

osg实现自定义插件读取自定义格式的模型文件到场景

目录 1. 前言 2. 预备知识 3. 工具、原料 4. 代码实现 1. 前言 osg提供了很多插件来读取模型文件到场景中&#xff0c;这些插件支持大约70种格式类型的文件&#xff0c;但现实中的文件是各式各样&#xff0c;osg不可能囊括所有类型文件&#xff0c;当osg不支持某种类型格式…