设计模式_责任链模式_Chain

案例引入

学校OA系统的采购审批项目: 需求是

  • 采购员采购教学器材
  • 如果金额 小于等于5000(0<x<=5000),由教学主任审批
  • 如果金额 小于等于10000(5000<x<=10000),由院长审批
  • 如果金额 小于等于30000(10000<x<=30000),由副校长审批
  • 如果金额 超过30000以上(30000<x),由校长审批

传统方式实现

创建一个不同的审批人对应的类,在客户端中写 if else 判断程序,符合不同的条件,就让不同的审批人去处理

分析

客户端这里会使用到分支判断(比如switch)来对不同的采购请求处理, 这样就存在如下问题

  • 如果各个级别的人员审批金额发生变化,客户端的代码也需要发生变化
  • 客户端必须明确知道有多少个审批级别及其访问方式,这样对一个采购请求进行处理的类和Approver (审批人) 就存在强合耦合关系,不利于代码的扩展和维护

【改进】

使用职责链模式

介绍

基础介绍

  • 职责链模式又叫责任链模式,为请求创建了一个接收者对象的链(如下方的简单示意图)。这种模式对请求的发送者和接收者进行解耦。当发送者发送一个请求之后,接收者会按照职责链的顺序一个一个地找出到底应该由谁来负责处理这个请求

在这里插入图片描述

  • 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推,如果所有人都不能处理,最后就会提示或者报错说不能处理
  • 这种类型的设计模式属于行为型模式

登场角色

在这里插入图片描述

  • Handler(抽象处理者):定义了一个处理请求的接口,同时设有变量来保存其他的Handler
  • ConcreteHandler(具体处理者):处理它自己负责的请求,可以访问它的后继者(即下一个处理者),如果可以处理当前请求则处理,否则就将该请求交给后继者去处理,从而形成一个职责链
  • Request(请求对象):含有很多属性,表示一个请求
  • Client(请求者):发送请求

应用场景

  • 有多个对象可以处理同一个请求时,比如: 多级请求、请假/加薪等审批流程、Java Web中Tomcat对Encoding的处理、拦截器

案例实现

案例一

类图

在这里插入图片描述

实现

【采购请求】

package com.test.responsibilitychain;/**
* 请求类
*/
public class PurchaseRequest {/**
* 请求类型
*/
private int type = 0;
/**
* 请求金额
*/
private float price = 0.0f;
private int id = 0;/**
* 构造器
* @param type
* @param price
* @param id
*/
public PurchaseRequest(int type, float price, int id) {
this.type = type;
this.price = price;
this.id = id;
}
public int getType() {
return type;
}
public float getPrice() {
return price;
}
public int getId() {
return id;
}}

【抽象请求处理者】

package com.test.responsibilitychain;/**
* 请求处理者
*/
public abstract class Approver {/**
* 下一个处理者
*/
Approver approver;
/**
* 当前处理者名字
*/
String name;public Approver(String name) {
this.name = name;
}/**
* 下一个处理者
* @param approver
*/
public void setApprover(Approver approver) {
this.approver = approver;
}/**
* 处理审批请求的方法,得到一个请求, 处理是子类完成,因此该方法做成抽象
* @param purchaseRequest
*/
public abstract void processRequest(PurchaseRequest purchaseRequest);}

【系级处理人】

package com.test.responsibilitychain;public class DepartmentApprover extends Approver {public DepartmentApprover(String name) {
super(name);
}@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if(purchaseRequest.getPrice() <= 5000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}}

【学院级别处理人】

package com.test.responsibilitychain;public class CollegeApprover extends Approver {public CollegeApprover(String name) {
super(name);
}@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if(purchaseRequest.getPrice() < 5000 && purchaseRequest.getPrice() <= 10000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}

【副校长】

package com.test.responsibilitychain;public class ViceSchoolMasterApprover extends Approver {public ViceSchoolMasterApprover(String name) {
super(name);
}@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if(purchaseRequest.getPrice() < 10000 && purchaseRequest.getPrice() <= 30000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}

【校长】

package com.test.responsibilitychain;public class SchoolMasterApprover extends Approver {public SchoolMasterApprover(String name) {
super(name);
}@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if(purchaseRequest.getPrice() > 30000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}

【客户端】

package com.test.responsibilitychain;public class Client {public static void main(String[] args) {
//创建一个请求
PurchaseRequest purchaseRequest = new PurchaseRequest(1, 31000, 1);//创建相关的审批人
DepartmentApprover departmentApprover = new DepartmentApprover("张主任");
CollegeApprover collegeApprover = new CollegeApprover("李院长");
ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校");
SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("佟校长");//需要将各个审批级别的下一个设置好 (处理人构成环形)
departmentApprover.setApprover(collegeApprover);
collegeApprover.setApprover(viceSchoolMasterApprover);
viceSchoolMasterApprover.setApprover(schoolMasterApprover);
//防止一千块钱也来找校长处理,找校长的话,校长就去找手下处理
schoolMasterApprover.setApprover(departmentApprover);//找谁开始调用都可以实现功能
departmentApprover.processRequest(purchaseRequest);
viceSchoolMasterApprover.processRequest(purchaseRequest);
}}

【主类】

 请求编号 id= 1 被 佟校长 处理
请求编号 id= 1 被 佟校长 处理Process finished with exit code 0

案例二

类图

在这里插入图片描述

实现

【问题类】

package com.test.responsibilitychain.Sample;public class Trouble {
/**
* 问题编号
*/
private int number;/**
* 根据编号生成问题
* @param number
*/
public Trouble(int number) {    
this.number = number;
}/**
* 获取问题编号
* @return
*/
public int getNumber() {        
return number;
}/**
* 代表问题的字符串
* @return
*/
public String toString() {      
return "[Trouble " + number + "]";
}
}

【用来解决问题的抽象类】

package com.test.responsibilitychain.Sample;public abstract class Support {
/**
* 解决问题的实例的名字
*/
private String name;
/**
* 要推卸给的对象
*/
private Support next;/**
* 生成解决问题的实例
*
* @param name
*/
public Support(String name) {
this.name = name;
}/**
* 设置要推卸给的对象
*
* @param next
* @return
*/
public Support setNext(Support next) {
this.next = next;
return next;
}/**
* 解决问题的步骤
* 调用了抽象方法resolve,这里属于模板方法模式
* @param trouble
*/
public void support(Trouble trouble) {
if (resolve(trouble)) {
done(trouble);
} else if (next != null) {
// 问题还没有解决,让下一个对象来尝试解决
next.support(trouble);
} else {
// 所有对象都没有办法解决该问题,提示用户 问题没办法解决
fail(trouble);
}
}/**
* 显示字符串
*
* @return
*/
public String toString() {
return "[" + name + "]";
}/**
* 解决问题的方法
* 需要子类去具体实现,如果解决了问题,就返回true
*
* @param trouble
* @return
*/
protected abstract boolean resolve(Trouble trouble);/**
* 解决
*
* @param trouble
*/
protected void done(Trouble trouble) {
System.out.println(trouble + " is resolved by " + this + ".");
}/**
* 未解决
*
* @param trouble
*/
protected void fail(Trouble trouble) {
System.out.println(trouble + " cannot be resolved.");
}
}

【不解决问题的类】

package com.test.responsibilitychain.Sample;public class NoSupport extends Support {
public NoSupport(String name) {
super(name);
}/**
* 解决问题的方法 
* 什么都解决不了,直接返回false
* @param trouble
* @return
*/
protected boolean resolve(Trouble trouble) {     
return false; 
}
}

【具体解决问题的类:只要问题的编号小于limit,就可以解决】

package com.test.responsibilitychain.Sample;/**
* 可以解决编号小于limit的问题
*/
public class LimitSupport extends Support {
private int limit;/**
* 构造函数
*
* @param name
* @param limit
*/
public LimitSupport(String name, int limit) {
super(name);
this.limit = limit;
}protected boolean resolve(Trouble trouble) {
if (trouble.getNumber() < limit) {
/*
解决了什么什么问题
*/
return true;
} else {
return false;
}
}
} 

【具体解决问题的类:只要问题的编号是奇数,就可以解决】

package com.test.responsibilitychain.Sample;public class OddSupport extends Support {
/**
* 构造函数
* @param name
*/
public OddSupport(String name) {                
super(name);
}
protected boolean resolve(Trouble trouble) {    
if (trouble.getNumber() % 2 == 1) {
return true;
} else {
return false;
}
}
}

【具体解决问题的类:只能解决指定编号的问题】

package com.test.responsibilitychain.Sample;public class SpecialSupport extends Support {
private int number;/**
* 构造函数
*
* @param name
* @param number
*/
public SpecialSupport(String name, int number) {
super(name);
this.number = number;
}/**
* 解决问题的方法
*
* @param trouble
* @return
*/
protected boolean resolve(Trouble trouble) {
if (trouble.getNumber() == number) {
return true;
} else {
return false;
}
}
}

【主类】

package com.test.responsibilitychain.Sample;public class Main {
public static void main(String[] args) {
// 生成六个解决问题的实例
Support alice = new NoSupport("Alice");
Support bob = new LimitSupport("Bob", 100);
Support charlie = new SpecialSupport("Charlie", 429);
Support diana = new LimitSupport("Diana", 200);
Support elmo = new OddSupport("Elmo");
Support fred = new LimitSupport("Fred", 300);
// 连接对象,形成职责链
alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred);
// 制造各种问题,增长步长大一点,让问题的编号更加的分散
for (int i = 0; i < 500; i += 33) {
alice.support(new Trouble(i));
}
}
}

【运行】

[Trouble 0] is resolved by [Bob].
[Trouble 33] is resolved by [Bob].
[Trouble 66] is resolved by [Bob].
[Trouble 99] is resolved by [Bob].
[Trouble 132] is resolved by [Diana].
[Trouble 165] is resolved by [Diana].
[Trouble 198] is resolved by [Diana].
[Trouble 231] is resolved by [Elmo].
[Trouble 264] is resolved by [Fred].
[Trouble 297] is resolved by [Elmo].
[Trouble 330] cannot be resolved.
[Trouble 363] is resolved by [Elmo].
[Trouble 396] cannot be resolved.
[Trouble 429] is resolved by [Charlie].
[Trouble 462] cannot be resolved.
[Trouble 495] is resolved by [Elmo].Process finished with exit code 0

职责链模式在Spring中的应用

在这里插入图片描述

  • springmvc请求的流程图中,执行了拦截器相关方法interceptor.preHandler等等,在处理SpringMvc请求时,使用到职责链模式,还使用到适配器模式
  • HandlerExecutionChain主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只是将请求分配给链上注册处理器执行,这是职责链实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程
  • HandlerExecutionChain维护了Handlerlnterceptor的集合, 可以向其中注册相应的拦截器

在这里插入图片描述

总结

【优点】

  • 将请求和处理分开,实现解耦,提高系统的灵活性
  • 简化了对象,使对象不需要知道链的结构
  • 可以动态地调整链的结构
  • 让每一个ConcreteHandler更加专注于自己的工作,程序的逻辑更加清晰

【缺点】

  • 性能会受到影响,特别是在链比较长的时候。因此需控制链中最大节点数量,一般通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值超过则不允许该链建立,避免出现超长链破坏系统性能
  • 调试不方便,采用了类似递归的方式,调试时逻辑可能比较复杂

【问答】

在视窗系统中,经常使用职责链模式。在视窗系统的窗口中,有按钮和文本输入框、勾选框等组件(也称为部件或控件)。当点击鼠标时,鼠标点击事件的处理是如何传播的呢? 职责链模式中的next(要推卸给的对象)是哪个组件呢?

答:一般next字段中保存的多是控件的父窗口

如下图中的小对话框。当焦点移动至“字体”列表框上时,按下键盘上的↑↓键可以选择相应的字体。但是,当焦点移动至“显示均衡字体”勾选框上时如果按下↑键,焦点会移动至“字体”列表框,之后,即使按下↓键,焦点也不会返回到勾选框上。请运用Chain ofResponsibility模式的思考方法来说明这个问题

在这里插入图片描述

如果焦点在列表框中,列表框会自己处理↑↓键被按下的事件,不会将请求推卸给next,但如果焦点在勾选框中,它则不会自己处理↑↓小键被按下的事件,而是将请求推卸给next所对应的父对话框。当父对话框接收到↑↓键被按下的事件时,会将焦点移动至列表框中在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Springboot的考编论坛网站的设计与实现(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的考编论坛网站的设计与实现&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层…

二、SSM 整合配置实战

本章概要 依赖整合和添加控制层配置编写(SpringMVC 整合)业务配置编写(AOP/TX 整合)持久层配置编写(MyBatis 整合)容器初始化配置类整合测试 2.1 依赖整合和添加 数据库准备 数据库脚本 CREATE DATABASE mybatis-example;USE mybatis-example;CREATE TABLE t_emp(emp_id INT…

投稿文件准备复盘

8月底 1、实验做完&#xff0c;数据产出后&#xff0c;开始分析数据&#xff0c;讨论&#xff0c;分析&#xff0c;讨论&#xff0c;补充实验 12月底 开始着手于投稿论文 2.1、上传数据到GEO&#xff0c;要到GEO号&#xff08;1周左右如果不打回的话&#xff09; 2.2、首先…

代码随想录算法训练营第二七天 | 回溯 组合 分割

目录 组合总和组合总和II分割回文串 LeetCode 39. 组合总和 LeetCode 40.组合总和II LeetCode 131.分割回文串 组合总和 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &am…

寒假作业5

TCP 1&#xff1a;提供面向连接的&#xff0c;可靠的数据传输服务 2&#xff1a;传输过程中&#xff0c;数据无误、数据无丢失、数据无失序、数据无重复 3&#xff1a;数据传输效率低&#xff0c;耗费资源多 4&#xff1a;数据收发是不同步的 5&#xff1a;TCP的使用场景&#…

SpringBoot:多环境配置

多环境配置demo代码&#xff1a;点击查看LearnSpringBoot02 点击查看更多的SpringBoot教程 方式一、多个properties文件配置 注意&#xff1a;创建properties文件,命名规则&#xff1a;application-&#xff08;环境名称&#xff09; 示例&#xff1a;application-dev.proper…

WINDOWS搭建NFS服务器

下载并安装 Networking Software for Windows 启动配置 找到安装目录&#xff08;如C:\Program Files\nfsd&#xff09;&#xff0c;双击nfsctl.exe&#xff0c;菜单Edit->Preferences 启动后&#xff1a; 配置Export Exports->Edit exports file 其他的几句我都删除…

Linux-3 进程概念(三)

1.环境变量 1.1基本概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如&#xff1a;我们在编写C/C代码的时候&#xff0c;在链接的时候&#xff0c;从来不知道我们的所链接的动态静态库在哪里&#xff0c;但是照样可以链接成功…

2 月 5 日算法练习- 字符串

人物相关性分析 思路&#xff1a;枚举前缀和。枚举字符串中的 Bob 位置利用前缀和来记录&#xff0c;然后枚举 Alice 的位置&#xff0c;通过判断 Bob 在 Alice 前面还是后面来进行不同的前缀和差值计算距离 k 距离中 Bob 的个数求和就是答案&#xff0c;复杂度是 On。注意 Bob…

探索C语言结构体:编程中的利器与艺术

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C语言学习 贝蒂的主页&#xff1a;Betty‘s blog 1. 常量与变量 1. 什么是结构体 在C语言中本身就自带了一些数据类型&#x…

大数据学习之Redis,十大数据类型的具体应用(五)

目录 3.9 Redis地理空间&#xff08;GEO&#xff09; 简介 原理 Redis在3.2版本以后增加了地理位置的处理哦 命令 命令实操 如何获得某个地址的经纬度 3.9 Redis地理空间&#xff08;GEO&#xff09; 简介 移动互联网时代LBS应用越来越多&#xff0c;交友软件中附近的…

今日arXiv最热NLP大模型论文:大语言模型为什么始终会产生幻觉

随着LLMs的广泛应用&#xff0c;幻觉问题引起了越来越多的安全和道德关注&#xff0c;各种各样的幻觉缓解方法也层出不穷&#xff0c;比如各类知识增强方法、对模型答案进行验证、新的评估基准等。 相信大家会和我一样有一个疑问&#xff1a;幻觉问题有望被彻底解决吗&#xf…