目录
责任链模式【Chain of Responsibility Pattern】,什么是责任链模式?核心思想?结构?作用?优缺点?实现案例?
什么是责任链模式?
责任链模式是一种行为型设计模式,它允许多个处理对象(处理器)有机会处理请求,而无需指定请求的具体接收者。处理器形成一条链,每个处理器都包含对下一个处理器的引用,当一个处理器无法处理请求时,它会将请求传递给链中的下一个处理器。责任链模式的核心思想是将请求的发送者与接收者解耦,使得多个对象都有机会处理请求。
责任链模式的核心思想
责任链模式通过将请求的处理逻辑进行解耦,让请求在一系列处理器中传递,直到找到能够处理它的对象,或者到达链的末尾。每个处理器既可以处理请求,也可以将其传递给下一个处理器,从而形成一种灵活的处理流程。
责任链模式的结构
(1)抽象处理者(Handler)
定义了处理请求的接口,并保存对下一个处理者的引用(通常通过设置 setNextHandler() 来传递请求)。抽象处理者通常包含一个处理请求的方法,如 handleRequest()。
(2)具体处理者(ConcreteHandler)
继承自抽象处理者,具体实现了处理请求的逻辑。如果当前处理者无法处理请求,则将请求传递给下一个处理者。
(3)客户端(Client)
发起请求的对象,客户端只需将请求发送给链中的第一个处理者,而不需要关心请求会被哪个处理者处理。
责任链模式的UML类图
+------------------+ +--------------------+ +--------------------+
| Handler | | ConcreteHandler1 | | ConcreteHandler2 |
|------------------| |--------------------| |--------------------|
| + setNextHandler()| | + handleRequest() | | + handleRequest() |
| + handleRequest()|----->| + setNextHandler() |----->| + setNextHandler() |
+------------------+ +--------------------+ +--------------------+
责任链模式的作用
(1)解耦请求的发送者与接收者
请求发送者只需知道链的第一个处理者,而不需要知道最终由谁处理请求。
(2)灵活地分配职责
通过动态设置责任链的顺序,责任链模式允许灵活地调整处理请求的责任分配。
(3)请求的链式传递
每个处理者都有机会处理请求,处理逻辑可以分布在多个对象中。
责任链模式的优缺点
优点
(1)降低耦合度
请求的发送者和处理者解耦,发送者无需知道是哪个处理者处理了请求。
(2)增强系统的灵活性
可以在运行时动态地调整责任链的结构(如:增加、删除或更改处理者的顺序)。
(3)符合单一职责原则
每个处理者只需处理自己能处理的部分,其他部分传递给下一个处理者,职责单一明确。
缺点
(1)可能导致请求无法处理
如果责任链没有合适的处理者,可能会导致请求无法得到处理,必须在设计时考虑这个问题。
(2)调试困难
由于请求可能经过多个处理者传递,责任链可能比较长,调试时难以跟踪请求的执行过程。
(3)性能开销
链条中的每个处理者都需要对请求进行处理或传递,链条过长可能导致性能问题。
责任链模式的应用场景
(1)审批流程
如上例所示,责任链模式广泛应用于各类审批系统,不同权限的审批者处理不同额度的请求。比如企业内部的请假审批、费用报销审批等。
(2)事件处理机制
在用户界面或操作系统中,事件(如点击、拖拽等)可以由责任链中的多个对象处理,如按钮、窗口、操作系统等。一个组件无法处理事件时,可以将事件传递给下一个组件。
(3)日志处理
责任链模式可以用于日志系统,不同的日志处理器可以处理不同等级的日志。例如,错误日志可以记录到文件中,调试日志则可以输出到控制台。
(4)Web请求处理
责任链模式在Web服务器中常用于请求的处理管道,如过滤器链(Filter Chain)。每个过滤器负责处理请求的某个方面(如权限验证、参数校验等),如果当前过滤器无法处理,可以将请求传递给下一个过滤器。
责任链模式的典型应用
(1)Java Servlet 过滤器链
在 Java 的 Servlet 技术中,过滤器链就是责任链模式的典型应用。多个过滤器可以按顺序处理一个请求,最终请求到达目标 Servlet 或 JSP。
(2)权限校验系统
在用户权限校验系统中,责任链模式可以用于不同级别的权限校验。每个处理者负责校验某一层级的权限,不通过时传递给下一个处理者。
责任链模式实现案例
中国古代对妇女制定了“三从四德”的道德规范,“三从”是指“未嫁从父、既嫁从夫、夫死从子”,也就是说一个女性,在没有结婚的时候要听从于父亲,结了婚后听从于丈夫,丈夫死了还要听儿子的。
举个例子来说,一个女的要出去逛街,同样这样的一个请求,在她没有出嫁前她必须征得父亲的同意,出嫁之后必须获得丈夫的许可,那丈夫死了怎么办?一般都是男的比女的死的早,还要问问儿子是否允许自己出去逛街,估计你下边马上要问要是没有儿子怎么办?请示小叔子、侄子等等,在父系社会中,妇女只占从属地位,现在想想中国的妇女还是比较悲惨的,逛个街还要请示来请示去,而且作为父亲、丈夫、儿子只有两种选择:要不承担起责任来告诉她允许或不允许逛街,要不就让她请示下一个人,这是整个社会体系的约束,应用到我们项目中就是业务规则,那我们来看怎么把“三从”通过我们的程序来实现,需求很简单:通过程序描述一下古代妇女的“三从”制度。
先看类图:
1、定义一个女性接口
package com.uhhe.common.design.chain;
/**
* 古代悲哀女性的总称
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 9:55
*/
public interface IWomen {
/**
* 获得个人状况
*
* 1---未出嫁
* 2---出嫁
* 3---夫死
*
* @return 级别
*/
int getType();
/**
* 获得个人请示,你要干什么?出去逛街?约会?还是看电影
*
* @return 请示内容
*/
String getRequest();
}
2、女性实现类
package com.uhhe.common.design.chain;
/**
* 女性实现类
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 10:02
*/
public class Women implements IWomen {
private int type = 0;
private String request;
public Women(int type, String request) {
this.type = type;
// 为了显示好看点,在这里做了点处理
switch (this.type) {
case 1:
this.request = "女儿的请求是:" + request;
break;
case 2:
this.request = "妻子的请求是:" + request;
break;
case 3:
this.request = "母亲的请求是:" + request;
default:
break;
}
}
@Override
public int getType() {
return this.type;
}
@Override
public String getRequest() {
return this.request;
}
}
3、定义有处理权的人员抽象类
package com.uhhe.common.design.chain;
/**
* 父系社会,那就是男性有至高权利,handler控制权
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 9:54
*/
public abstract class Handler {
/**
* 能处理的级别
*/
private int level;
/**
* 责任传递,下一个人责任人是谁
*/
private Handler nextHandler;
public Handler(int level) {
// 每个类都要说明一下自己能处理哪些请求
this.level = level;
}
/**
* 一个女性(女儿,妻子或者是母亲)要求逛街,你要处理这个请求
*
* @param women 女人
*/
public final void handlerMessage(IWomen women) {
if (women.getType() == this.level) {
this.response(women);
} else {
if (this.nextHandler != null) {
// 有后续环节,才把请求往后递送
this.nextHandler.handlerMessage(women);
} else {
// 已经没有后续处理人了,不用处理了
System.out.println("-----------没地方请示了,不做处理!---------\n");
}
}
}
/**
* 如果你属于你处理的返回,你应该让她找下一个环节的人
* 比如: 女儿出嫁了,还向父亲请示是否可以逛街,那父亲就应该告诉女儿,应该找丈夫请示
*/
public void setNext(Handler handler) {
this.nextHandler = handler;
}
/**
* 有请示那当然要回应
*
* @param women 女人
*/
public abstract void response(IWomen women);
}
4、有权处理的三个实现类(父亲、丈夫、儿子)
package com.uhhe.common.design.chain;
/**
* 父亲类
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 9:58
*/
public class Father extends Handler {
public Father() {
// 父亲只能处理请求类型为1的请求
super(1);
}
@Override
public void response(IWomen women) {
System.out.println("--------女儿向父亲请示-------");
System.out.println("类型:" + women.getType() + "\t" + women.getRequest());
System.out.println("父亲的答复是: 同意\n");
}
}
package com.uhhe.common.design.chain;
/**
* 丈夫类
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 9:59
*/
public class Husband extends Handler {
public Husband() {
// 丈夫只能处理请求类型为2的请求
super(2);
}
@Override
public void response(IWomen women) {
System.out.println("--------妻子向丈夫请示-------");
System.out.println("类型:" + women.getType() + "\t" + women.getRequest());
System.out.println("丈夫的答复是:同意\n");
}
}
package com.uhhe.common.design.chain;
/**
* 儿子类
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 10:00
*/
public class Son extends Handler {
public Son() {
// 儿子只能处理请求类型为 3
super(3);
}
@Override
public void response(IWomen women) {
System.out.println("--------母亲向儿子请示-------");
System.out.println("类型:" + women.getType() + "\t" + women.getRequest());
System.out.println("儿子的答复是:同意\n");
}
}
这三个类都很简单,构造方法是必须实现的,通过构造方法我们设置了各个类能处理的请求类型,Father 只能处理请求类型为 1(也就是女儿)的请求;Husband只能处理请求类型类 2(也就是妻子)的请求;儿子只能处理请求类型为 3(也就是目前)的请求。
5、去请求逛街
package com.uhhe.common.design.chain;
import java.util.ArrayList;
import java.util.Random;
/**
* 后人来看这样的社会道德
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 10:05
*/
@SuppressWarnings("all")
public class Client {
/**
* 责任链模式【Chain of Responsibility Pattern】
* <p>
* 责任链模式屏蔽了请求的处理过程,你发起一个请求到底是谁处理的,这个你不用关心,只要你把请求抛给责任链的第一个处理者,
* 最终会返回一个处理结果(当然也可以不做任何处理),作为请求者可以不用知道到底是需要谁来处理的,这是责任链模式的核心。
* <p>
* 责任链有一个缺点:调试不是很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。
* <p>
* 同时责任链模式也可以做为一种补救模式来使用,举个简单例子,如项目开发的时候,需求确认是这样的:
* 一个请求(比如银行客户存款的币种),一个处理者(只处理人民币),但是随着业务的发展(改革开放了嘛,还要处理美元、日元等等),
* 处理者的数量和类型都有所增加,那这时候就可以在第一个处理者后面建立一个链,也就是责任链来处理请求,
* 你是人民币,好,还是第一个业务逻辑来处理,
* 你是美元,好,传递到第二个业务逻辑来处理,
* 日元,欧元…,
* 这些都不用在对原有的业务逻辑产生很大改变,通过扩展实现类就可以很好的解决这些需求变更的问题。
*/
public static void main(String[] args) {
// 随机挑选几个女性
Random rand = new Random();
ArrayList<IWomen> arrayList = new ArrayList();
for (int i = 0; i < 5; i++) {
arrayList.add(new Women(rand.nextInt(4), "我要出去逛街"));
}
// 定义三个请示对象
Handler father = new Father();
Handler husband = new Husband();
Handler son = new Son();
// 设置请示顺序
father.setNext(husband);
husband.setNext(son);
for (IWomen women : arrayList) {
father.handlerMessage(women);
}
}
}
总结:
责任链模式通过将请求的发送者与接收者解耦,使得多个对象有机会处理请求,增强了系统的灵活性和扩展性。在需要处理请求的职责动态分配、审批系统、事件处理等场景中,责任链模式是一种非常有效的设计模式。