[设计模式]职责链(责任链)模式_职责目录链啥意思-程序员宅基地

技术标签: java  # 设计模式  设计模式  

github地址:https://github.com/1711680493

点我进入github

如需了解更多设计模式,请进入我的设计模式专栏


目录

 

职责链模式

定义

优点

缺点

结构

应用场景

扩展

实战

总结


 

 

可直接跳过此部分

有一段时间没有写设计模式方面的文章了,这次因为某种需求使用到了责任链模式,于是记录一下

 

首先讲一下我的需求

我在做一个文件上传的功能,也就意味着需要将接收到的数据保存起来

那么,保存在什么地方呢?

 

我的项目后续会放到多台电脑上(多台电脑跑一个程序),于是需要使用格外的机器来保存图片,这时使用到的是网络

但是目前我使用的是一台电脑,所以保存到本地,于是我想到了将保存地址放入配置文件中

 

这时,我需要一个解析地址的方法

比如我输入 http://xxx.xxx 就返回 HTTP 的工具对象,输入 C:/xxx 就返回本地文件对象

具体不在乎是什么对象,只需要能够输入(input)/输出(output) 即可

 

最开始想到使用解释器模式,最终发现责任链模式更为合适

 

概述

可以想象一下需求,我们输入一串字符串,然后这个字符串依次经过处理类,最终返回指定对象

一个职责链就如同其名一样,一条链子,所以都是依次被执行,每一个对象有机会

 

例如上面的例子,假设有类 HTTP 用来判断是否为http链接,File 用来判断是否为本地地址,那么流程如下

客户端输入链接->HTTP处理 不能处理/不是->File处理

                               ↓ 可以处理                          ↓可以处理

                              返回对应对象                    返回对应对象

 

但是按照上面这样,比如我增加 https,则会经过 File 在处理 https,性能就会降低

 

于是我想到了分级(树结构),策略模式+职责链模式完成我的需求

就好比 有专门做Java开发的和专门做 C# 开发的,做Java开发的主程接到需求,这需求是用C#开发xxx,就直接把需求丢给C#主程

但是需求是Java的,自己有不会或者不想搞,就丢给别的Java开发

 

以上,我将此功能写在了我的Java工具包中 https://github.com/1711680493/ShendiKit 对应 shendi.kit.path.URL

 

 


 

职责链模式

职责链模式又叫责任链模式(Chain of Responsibility)

把请求从链中的一个对象传到下一个对象,直到请求被响应为止,通过这种方式去除对象之间的耦合

 

定义

为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成的一条链,

当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止

在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了

责任链模式是一种对象行为型模式

 

优点

降低了对象之间的耦合度,该模式使得一个对象无需知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无需拥有对方的明确信息

增强了系统的可扩展性,可以根据需要增加新的请求处理类,满足开闭原则

增强了给对象指派职责的灵活性,当工作流程发生变化,可以动态地改变链内成员或者调动它们的次序,也可动态地新增或者删除责任

责任链简化了对象之间的链接,每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多if,或者if else语句

责任分担,每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则

 

缺点

不能保证每个请求一定被处理,由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理

对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响

职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用

 

通常情况下,可以通过数据链表来实现职责链模式的数据结构

 

结构

抽象处理者角色: 定义处理请求的接口,包含抽象处理方法和一个后继链接

具体处理者角色: 实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给他的后继者

客户类角色: 创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程

 

应用场景

有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定

可动态指定一组对象处理请求,或添加新的处理对象

在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求

 

扩展

  • 纯的职责链模式:一个请求必须被某一个处理者对象接收,且一个具体处理者对某个请求的处理只能采用两种行为之一,自己处理(承担责任);把责任推给下家处理;
  • 不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收

 

实战

通过传递字符串来演示一下


/**
 * 职责链模式
 * @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
 * @version 1.0
 */
public class ChainOfResponsibility {

	public static void main(String[] args) {

		Handler ha = new HandlerA();
		Handler hb = new HandlerB();
		Handler hbc = new HandlerBC();
		
		ha.h = hb;
		hb.h = hbc;
		
		ha.handler("ABC");
	}
	
}

/**
 * 抽象处理者
 * @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
 * @version 1.0
 */
abstract class Handler {
	
	protected Handler h;
    public void setHandler(Handler h) {
    	this.h = h;
    }
    
    public abstract void handler(String req);
	
}

/**
 * 具体处理者,只处理A
 * @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
 * @version 1.0
 */
class HandlerA extends Handler {

	@Override
	public void handler(String req) {
		if (req.contains("A")) {
			System.out.println("有A");
			// 如果还有别的则交给下一个执行
			if (req.contains("B") || req.contains("C")) {
				if (h != null) h.handler(req);
			}
		} else {
			if (h != null) h.handler(req);
		}
	}
	
}

/**
 * 具体处理者,只处理AB
 * @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
 * @version 1.0
 */
class HandlerB extends Handler {

	@Override
	public void handler(String req) {
		if (req.contains("B")) {
			System.out.println("有B");
			// 如果还有别的则交给下一个执行
			if (req.contains("A") || req.contains("C")) {
				if (h != null) h.handler(req);
			}
		} else {
			if (h != null) h.handler(req);
		}
	}
	
}

/**
 * 具体处理者,只处理BC
 * @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
 * @version 1.0
 */
class HandlerBC extends Handler {

	@Override
	public void handler(String req) {
		if (req.contains("BC")) {
			System.out.println("有BC");
			// 如果还有别的则交给下一个执行
			if (req.contains("A") || req.contains("B")) {
				if (h != null) h.handler(req);
			}
		} else {
			if (h != null) h.handler(req);
		}
	}
	
}

 

输出如下 

 

总结

职责链模式由客户端创建处理链(将对象创建,给设置下一个链是什么)后调用第一个链来进行处理

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_41806966/article/details/111594238

智能推荐

Spring Boot 2 之 WebFlux 反应式编程解析及实战_webflux底层原理-程序员宅基地

文章浏览阅读793次。近些年来,反应式编程亦或称为响应式编程,在开发者社区中很受欢迎,特别在 Spring 5 以及 Spring Boot 2 发布之后热度再次飙升。以反应式编程为基础的 Spring WebFlux 组件作为异步非阻塞的系统解决方案,可以明显的提高系统吞吐量。首先看一下Srping官网上的一张图,对比一下SpringMvc和Spring WebFlux:Spring WebFlux 提供了两种..._webflux底层原理

利用commons-fileupload 上传图片(包含表单数据)_使用commons组件实现图片文件上传-程序员宅基地

文章浏览阅读334次。在一个表单中包含普通文本数据,另外还有需要上传的图片,那么本程序将图片保存到服务器上的一个图片目录中,文本数据则获取然后输出,查看传输是否正确,后面的处理为涉及。上传的jsp页面:最后将信息输出:(图片已经保存在特定目录中)需要用到的两个jar包,commons-fileupload-1.2.2.jar commons-io.jarjsp页面:&lt;%@ page language="java..._使用commons组件实现图片文件上传

用程序来自动建立FTP帐号(serv-u的odbc设置)_ftp server manager自动创建账号-程序员宅基地

文章浏览阅读7.5k次。步骤:1、建立数据库(可以用任何数据库SQL SERVER,ACCESS均可)2、建立DNS3、安装Serv-U4、建立域5、完成了。呵呵~!serv-u build 6.0.0.1版本 ★ Serv-U FTP Server 6.0.0.1 final非常好的FTP服务器软件,它设置简单,功能强大,性能稳定。你现在就可以建立你自己的FTP服务器了。汉化说明:1、请先_ftp server manager自动创建账号

Qt中文帮助文档_qt5.9.1中文手册-程序员宅基地

文章浏览阅读5.2k次,点赞2次,收藏9次。截图 下载获取方式一:CSDN积分下载获取方式二:扫描下方微信公众号,回复 “112428046” ,立即获取链接。 关注笔者 - jxd微信公众号搜索 “码农总动员” 了解更多你不知道的XX,O(∩_∩)O..._qt5.9.1中文手册

3分钟学会使用Python推荐系统库Surprise_reader(line_format='user item rating timestamp', s-程序员宅基地

文章浏览阅读3.8k次,点赞2次,收藏24次。最近做推荐系统,研究了一下Surprise库,使用简单,效果不错。Github地址:NicolasHug/Surprise实现功能:找到和用户A相似的N个用户 找到和项目A相似的N个项目1.item_user_rate_time.txt 数据格式 user item rating timestamp (用户id 项目id 评分 时间戳)2.数据读取 训练模型impor..._reader(line_format='user item rating timestamp', sep=',', skip_lines=1)

23岁的一无所有,其实是理所应当的_我23岁可以选择一个单位干干一辈子吗-程序员宅基地

文章浏览阅读2.8w次,点赞9次,收藏15次。23 岁那年你正处在哪个状态?现在呢? 我,23岁,应届毕业生。生活,工作,爱情都处于人生的低谷,一穷二白,一无所有,一事无成。 分享一下成长的建议吧。匿名用户23岁那年...就是去年...... 在22岁的时候我毕业,同时第二年准备考研,结果因为压力太大,期望太高,又失利了,但是我依然满怀信心和憧憬 在我23岁那年四月,当我深爱的女孩(在这之前我追了她四年)说她要去北京时,我在毫无准备的情况下,..._我23岁可以选择一个单位干干一辈子吗

随便推点

使用jhlabs提供的类对图片进行滤镜处理_jhlabs 图片处理-程序员宅基地

文章浏览阅读1.8k次。public static void main(String[] args) throws IOException{ //调用的时候指定相关参数 AbstractBufferedImageOp bbf = new BoxBlurFilter((float)4,(float)3,3); System.out.println(System.getProperty("user.dir") +_jhlabs 图片处理

HIVE SQL中替换不可见字符的正则表达式_hive 替换不可见字符-程序员宅基地

文章浏览阅读1.7k次。正则进行替换后,仍旧出现字符串中存在不可见字符的情况。这往往是因为上有数据在解析过程中未将无法解析的非法字符剔除掉。表示匹配任何空白字符,包括空格、制表符、换页符等等, 等价于[ \f\n\r\t\v]。这个时候通常会使用正则表达式来替换字符串中的空白字符。函数,但是他们无法去除字符串中间的空格。对于剔除字符串首尾的空格,使用较多的是。在实际清洗过程中,发现即使使用了。进一步对不可见字符进行处理。_hive 替换不可见字符

常量表达式和constexpr_constexpr double inf = 1 >> 20;-程序员宅基地

文章浏览阅读161次。1、常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式,如下:const int max_i= 5; //max_i 是常量表达式int max_i = 5 //max_i不是常量表达式const int max_i = get_size(); //max_i不是常量表达式,因为其具体值只有在运行时才能获取到2、constexpr变量声明为constexpr的变量一定是一个常量,而且必须要用常量表达式初始化:constexpr int ..._constexpr double inf = 1 >> 20;

LAMP架构及部署_lamp.abc-程序员宅基地

文章浏览阅读637次。LAMP平台概述1.什么是LAMP目前最为成熟的一-种企业网站应用模式,可提供动态Web站点应用及开发环境2.构成组件Linux、Apache、 MySQL、 PHP/Per/Python3.LAMP的优势(1)成本低廉(2)可定制、易于开发(3)方便易用、安全和稳定 ..._lamp.abc

0023【Edabit ★☆☆☆☆☆】Using the “&&“ Operator-程序员宅基地

文章浏览阅读43次。【代码】0023【Edabit ★☆☆☆☆☆】Using the “&&“ Operator。

网络控制IO模块:WIZ220IO / WIZ220IO-EVB_嵌入式远程io网关-程序员宅基地

文章浏览阅读2.5k次。WIZ220IO是一款嵌入式远程 I/O模块,它能通过因特网在远程控制和监测I/O端口。可以使用Windows应用程序或嵌入式网络服务器检测并控制I/O端口。WIZ220IO支持16个数字I/O端口和4个模拟I/O。另外,固件和嵌入式网页也可以通过以太网进行远程更新。WIZ220IO-EVB 是评估底板,用户可用它迅速地评估WIZ220IO模块。该评估板有8个开关用于数字输入测试,8个LED用_嵌入式远程io网关

推荐文章

热门文章

相关标签