一文搞懂设计模式之责任链模式

大家好,我是晴天。我们又见面了,本周我们继续学习设计模式,本周将同大家一起学习责任链模式。

本文目录

场景引入

我们回想一下自己曾经经历过的考学场景,我们是如何一步一步考上大学的(为了简化过程只提取核心环节)。第一步,我们需要报名考试,第二步,我们需要去参加考试,第三步,我们才能够通过考试,顺利进入大学。这三个步骤必须要串行化执行,只有前一步通过了,我们才能进行后续的步骤,如果前一步没有通过,则提前终止整过流程,无需进行后续步骤了。
场景流程图

什么是责任链模式

责任链类图

责任链设计模式是一种行为型设计模式,它要求完成一件事情必须按照指定顺序进行,前一个环节完成了才能进行下一个环节,否则就提前终止。每个环节可以理解成一个链表的节点,当这个节点接收到上一个节点传送过来的信息时,开始进行自身的逻辑处理,处理完成后,传递给下一个节点,调用下一个节点的处理方法,直到链表遍历完成为止。

为什么需要责任链模式

责任链设计模式是一种行为型设计模式,它主要用于将请求沿着处理链传递,直到有一个处理者能够处理该请求为止。责任链模式有助于降低发送者和接收者之间的耦合度,使系统更加灵活和可扩展。

代码实战

实现顺序执行逻辑有两种处理方法,一种遍历方式,是将处理方法放到数组中,遍历处理方法即可,第二种就是使用责任链模式来处理。我们依次来看一下这两种实现方式。

遍历方式

package main

import (
	"context"
	"errors"
	"fmt"
)

type Student1 struct {
	Name     string
	SignUp   bool // 是否考试报名
	TakeTest bool // 是否参加考试
	PassExam bool // 是否通过考试
}

type handler1 func(ctx context.Context, stu *Student1) error

// 报名考试
func SignUp1(ctx context.Context, stu *Student1) error {
	if stu.SignUp {
		fmt.Println(stu.Name + "已经报名了考试")
		return errors.New(stu.Name + "已经报名了考试了")
	}
	stu.SignUp = true
	fmt.Println(stu.Name + "正在进行考试报名")
	return nil
}

// 参加考试
func TakeTest1(ctx context.Context, stu *Student1) error {
	if stu.TakeTest {
		fmt.Println(stu.Name + "已经参加了考试")
		return errors.New(stu.Name + "已经参加过考试了")
	}
	stu.TakeTest = true
	fmt.Println(stu.Name + "正在参加考试")
	return nil
}

// 通过考试
func PassExam1(ctx context.Context, stu *Student1) error {
	if stu.PassExam {
		fmt.Println(stu.Name + "已经通过了考试")
		return errors.New(stu.Name + "已经通过了考试")
	}
	stu.PassExam = true
	fmt.Println(stu.Name + "通过了考试")
	return nil
}

func main() {
	var handlers = []handler1{SignUp1, TakeTest1, PassExam1}
	var student = &Student1{
		Name: "张三",
	}
	for _, handler := range handlers {
		if err := handler(context.Background(), student); err != nil {
			fmt.Println(err)
		}
	}
}



// 输出:
张三正在进行考试报名
张三正在参加考试
张三通过了考试

代码解释

上述代码定义了三个方法,分别是报名考试(SignUp1)、参加考试(TakeTest)和通过考试(PassExam),这三种方法按照顺序,组成处理方法数组,依次对Student1进行处理,如果某个方法已经判断状态为 true 了,则说明该环节已经参与过了,不需要再进行后续环节的处理了。

责任链模式

package main

import "fmt"

// 责任链模式
/*
	定义学生考学场景:
 	step1:报名考试
	step2:参加考试
	step3:通过考试
*/

// 学生
type Student struct {
	Name     string
	SignUp   bool // 报名考试
	TakeTest bool // 参加考试
	PassExam bool // 通过考试
}

type Section interface {
	Do(s *Student)           // 参与改环节
	setNext(section Section) // 设置下一个环节
}

// 学生报名考试
type SignUp struct {
	next Section
}

func (s *SignUp) Do(stu *Student) {
	if stu.SignUp {
		fmt.Println(stu.Name + "报名考试成功")
		s.next.Do(stu)
		return
	}
	stu.SignUp = true
	fmt.Println(stu.Name + "考生正在报名...")
	s.next.Do(stu)
	return
}

func (s *SignUp) setNext(section Section) {
	s.next = section
}

// 学生参加考试
type TakeTest struct {
	next Section
}

func (p *TakeTest) Do(stu *Student) {
	if stu.TakeTest {
		fmt.Println(stu.Name + "参加考试")
		p.next.Do(stu)
		return
	}
	stu.TakeTest = true
	fmt.Println(stu.Name + "考生参加考试...")
	p.next.Do(stu)
	return
}

func (p *TakeTest) setNext(section Section) {
	p.next = section
}

// 学生通过考试
// 责任链最后一个节点,不需要请求后续节点的 Do 方法了
type PassExam struct {
	next Section
}

func (p *PassExam) Do(stu *Student) {
	if stu.PassExam {
		fmt.Println(stu.Name + "通过考试成功")
		return
	}
	stu.PassExam = true
	fmt.Println(stu.Name + "通过考试...")
	return
}

func (p *PassExam) setNext(section Section) {
	p.next = section
}

func main() {
	var student = &Student{
		Name:     "张三",
		SignUp:   false,
		TakeTest: false,
		PassExam: false,
	}
	// 最后一个节点
	var pe = &PassExam{}
	// 倒数第二个节点
	var pt = &TakeTest{}
	// 倒数第三个节点
	var su = &SignUp{}
	// 把责任链的节点连接起来
	pt.setNext(pe)
	su.setNext(pt)
	// 调用责任链的首个节点的执行方法
	su.Do(student)
}

代码解释

上述代码实现了一个具有三个节点的责任链,三个节点分别是SignUp(报名考试)、TakeTest(参加考试)、PassExam(通过考试),每个责任链的节点都是一个 Section 类型,每个节点都实现了两个方法,一个是执行自身逻辑的 Do 方法,另一个就是连接下一个节点的 setNext 方法。这里注意,最后一个节点 PassExam 不需要调用下一个节点了(已经是最后一个节点了)。

两种处理方式的对比

遍历方式优点

  1. 遍历方式结构简单,能够方便地将处理方法按照指定顺序,对请求进行处理。

遍历方式缺点

  1. 每个处理方法之间没有必然的联系,无法通过上一个处理方法找到写一个处理方法,关联性不强。

责任链模式优点

  1. 节点之间相互串联,关联性比较强,当前节点可以根据自身节点的处理结果来决定是否将请求继续向下传递。
  2. 执行一整条责任链的逻辑处理,只需要调用头节点处理方法即可,就像是只调用了一个节点的处理方法一样。

总结

本文,我们介绍了什么是责任链模式,为什么需要责任链模式,遍历方式和责任链模式的使用方法,并且对比了遍历方式和责任链模式的优缺点。希望各位读者小伙伴能有些许收获。

写在最后

感谢大家的阅读,晴天将继续努力,分享更多有趣且实用的主题,如有错误和纰漏,欢迎留言给予指正。 更多文章敬请关注作者个人公众号 晴天码字。 我们下期不见不散,to be continued…

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【超全】React学习笔记 中:进阶语法与原理机制

React学习笔记 React系列笔记学习 上篇笔记地址:【超全】React学习笔记 上:基础使用与脚手架 下篇笔记地址:【超全】React学习笔记 下:路由与Redux状态管理 React进阶组件概念与使用 1. React 组件进阶导读 在掌握了 React 的基…

【halcon】C# halcon 内存暴增

1 读取图片需要及时手动释放 一个6M的图片通过halcon进行加载&#xff0c;大约会消耗200M的内存&#xff0c;如果等待GC回收&#xff0c;而你又在不停的读取图片&#xff0c;你的内存占用&#xff0c;将在短时间内飙升。 2 halcon控件显示图片需要清空。 /// <summary>…

东南大学与OpenHarmony携手共建开源生态,技术俱乐部揭牌成立并迎来TSC专家进校园

11月25日,OpenAtom OpenHarmony(以下简称“OpenHarmony”)项目群技术指导委员会(以下简称“TSC”)与东南大学携手,于东南大学九龙湖校区金智楼一楼报告厅举办了“东南大学OpenHarmony技术俱乐部成立仪式暨OpenHarmony TSC专家进校园”活动。此次盛会标志着OpenHarmony开源社区和…

MATLAB 自抗扰控制 - Active Disturbance Rejection Control

系列文章目录 MATLAB 模型参考自适应控制 - Model Reference Adaptive Control 文章目录 系列文章目录前言一、控制器结构1.1 一阶逼近1.2 二阶逼近 二、指定控制器参数参考 前言 自抗扰控制 (ADRC) 是一种无模型控制方法&#xff0c;适用于为具有未知动态特性以及内部和外部…

【深度学习】Adversarial Diffusion Distillation,SDXL-Turbo 一步出图

代码&#xff1a; https://huggingface.co/stabilityai/sdxl-turbo 使用 SDXL-Turbo 是SDXL 1.0的精炼版本&#xff0c;经过实时合成训练。SDXL-Turbo 基于一种称为对抗扩散蒸馏 (ADD) 的新颖训练方法&#xff08;请参阅技术报告&#xff09;&#xff0c;该方法允许在高图像质…

linux防火墙NAT表原理及实操

目录 一、iptables保存规则 1、持久保存iptables规则 2、加载规则 3、开机自动重载规则 二、自定义链 1、创建自定义链 2、修改自定义链名 3、创建规则 4、删除自定义链 三、NAT表 1、SNAT 2、DNAT 一、iptables保存规则 使用iptables命令定义的规则&#xff0c;都…

数据链路层之网桥

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

linux查看进程_静态查看进程_动态查看进程

4.1.3 查看进程 4.1.3.1 静态查看进程&#xff1a;ps ps命令的基本语法如下&#xff1a; ps [options] 参数说明&#xff1a; ps 的参数非常多, 在此仅列出几个常用的参数并大略介绍含义 -A 列出所有的进程 -w 显示加宽可以显示较多的资讯 -au 显示较详细的资讯 -aux 显…

设计模式之道:解构结构型设计模式的核心原理

解构常见的三种结构型设计模式的核心原理 一、引言&#xff1a;如何学习设计模式&#xff1f;二、责任链模式2.1、代码结构2.2、符合的设计原则2.3、案例分析&#xff1a;nginx 阶段处理2.4、小结 三、装饰器模式3.1、代码结构3.2、符合的设计原则3.3、小结 四、组合模式4.1、代…

TLS协议握手流程

浅析 TLS&#xff08;ECDHE&#xff09;协议的握手流程&#xff08;图解&#xff09; - 知乎 前言 通过 wireshark 抓取 HTTPS 包&#xff0c;理解 TLS 1.2 安全通信协议的握手流程。 重点理解几个点&#xff1a; TLS 握手流程&#xff1a;通过 wireshark 抓取 HTTPS 包理解…

【iOS】数据持久化(三)之SQLite3及其使用

目录 数据库简介什么是SQLite&#xff1f;在Xcode引入SQLite APISQL语句的种类存储字段类型 SQLite的使用创建数据库创建表和删表数据表操作增&#xff08;插入数据INSERT&#xff09;删&#xff08;删除数据DELETE&#xff09;改&#xff08;更新数据UPDATE&#xff09;查&…

KEPserver和S7-200SMART PLC通信配置

KEPserver和S7-1200PLC通信配置,请查看下面文章链接: https://rxxw-control.blog.csdn.net/article/details/134683670https://rxxw-control.blog.csdn.net/article/details/134683670 1、OPC通信应用 2、选择Siemens驱动 3、添加S7-200设备

C语言内存函数memcpy、memmove、 memset、memcmp

--------------------------------------------- 夜色难免黑凉&#xff0c;前行必有曙光。 -------------今天我将带大家认识C语言中的内存函数 ---------的使用和模拟实现 -----这些函数的头文件依然被#include<string.h>所包含 目录 memcpy函数的使用 memcpy函数的…

css中的 Grid 布局

flex布局和grid布局区别 flex布局是 一维布局grid布局是二维布局 flex布局示例 grid布局示例 grid 布局初体验 体验地址 <div class"wrapper"><div class"one item">One</div><div class"two item">Two</div&…

NIO--07--Java lO模型详解

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 何为 IO?先从计算机结构的角度来解读一下I/o.再从应用程序的角度来解读一下I/O 阻塞/非阻塞/同步/异步IO阻塞IO非阻塞IO异步IO举例 Java中3种常见的IO模型BIO (Blo…

二分查找边界问题——排序数组找元素第一次出现和最后一次出现

二分查找的边界逼近问题&#xff1a; 下面的代码&#xff0c;第一个函数会向左边界逼近&#xff0c;第二个函数会像右边界逼近&#xff01; 考虑left5,right6这种情况&#xff0c;如果5&#xff0c;6的值都是满足的条件的怎么办&#xff1f; 如果mid(leftright1)/2&#xff0c;…

详解Spring中的Aop编程原理JDK动态代理和CGLIB动态代理

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…

HuggingFace学习笔记--BitFit高效微调

1--BitFit高效微调 BitFit&#xff0c;全称是 bias-term fine-tuning&#xff0c;其高效微调只去微调带有 bias 的参数&#xff0c;其余参数全部固定&#xff1b; 2--实例代码 from datasets import load_from_disk from transformers import AutoTokenizer, AutoModelForCaus…

【Pytorch】Visualization of Feature Maps(5)——Deep Dream

学习参考来自&#xff1a; PyTorch实现Deep Dreamhttps://github.com/duc0/deep-dream-in-pytorch 文章目录 1 原理2 VGG 模型结构3 完整代码4 输出结果5 消融实验6 torch.norm() 1 原理 其实 Deep Dream大致的原理和【Pytorch】Visualization of Feature Maps&#xff08;1&…

一起学docker系列之十七Docker Compose 与手动操作的比较与优势分析

目录 1 前言2 不使用 Docker Compose2.1 启动 MySQL 容器2.2 启动 Redis 容器2.3 启动微服务容器 3 使用 Docker Compose4 使用 Docker Compose 的优势5 结语参考地址 1 前言 在当今容器化应用的开发与部署中&#xff0c;容器编排工具的选择对于简化流程、提高效率至关重要。本…