责任链模式
# 责任链模式
# 一、责任链模式
原理:责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。
优缺点
- 1 优点
- 解耦责任对象:责任链模式将请求的发送者和接收者解耦,发送者无需知道具体的接收者是谁,也无需知道请求将由谁处理。
- 灵活性和可扩展性:由于责任链中的每个处理器都是独立的对象,因此可以根据需要轻松地添加、删除或调整处理器,而不会影响其他部分的代码。
- 单一职责原则:每个处理器只需关注自己能处理的请求,遵循了单一职责原则,使得代码更加清晰和易于维护。
- 动态组装处理器链:可以根据实际情况动态地组装责任链,根据请求的特征或条件决定由哪些处理器来处理请求。
- 降低耦合度:将请求的发送者和接收者解耦,使得系统的各个部分可以独立地进行修改、扩展或调整,降低了系统各模块之间的耦合度。
- 2 缺点
- 请求处理不确定性:由于请求的处理是由一系列处理器依次处理的,如果责任链较长或者处理器不当,可能导致请求处理的结果不确定性增加。
- 性能问题:责任链模式可能导致请求需要遍历整个责任链才能被处理,当责任链较长或者处理器的处理逻辑较复杂时,可能会影响性能。
- 可能造成循环引用:如果责任链中的某个处理器的逻辑错误导致调用了自身,可能会导致无限循环,甚至引发栈溢出错误。
- 难以调试:责任链模式的处理器是独立的,处理请求的具体逻辑可能分散在多个处理器中,这可能会增加调试的难度。
- 可能导致请求丢失:如果责任链中没有一个处理器能够处理请求,可能会导致请求被丢弃,而不是得到一个明确的处理结果。
适用场景
- 远程代理(Remote Proxy):用于控制对远程对象的访问。例如,RMI(远程方法调用)使用代理模式来实现客户端对远程对象的方法调用。
- 虚拟代理(Virtual Proxy):用于控制对资源密集型对象的访问。比如,在图片浏览器中,只有在真正需要显示图片时才去加载图片,而不是一次性加载所有图片。
- 延迟加载(Lazy Initialization):当创建一个对象非常耗时,而该对象在程序运行中不一定会被使用时,可以使用虚拟代理来延迟对象的创建,直到真正需要它时才进行创建。
保护代理(Protection Proxy) :用于控制对象的访问权限。可以在代理中对客户端的请求进行预处理,检查客户端是否具有访问目标对象的权限。例如,用户权限管理系统中,通过保护代理来控制不同用户对特定资源的访问权限。
智能代理(Smart Proxy):可以在访问对象时附加一些额外的功能,例如日志记录、性能监控、事务管理等。例如,在数据库操作中,可以通过代理模式实现自动的事务管理。
缓存代理(Cache Proxy):用于缓存某些操作的结果,以提高系统性能。例如,计算结果缓存代理可以缓存一些复杂计算的结果,避免每次都进行重复计算。
# 二、责任链模式基本代码
公共接口
public interface Handler { void handleRequest(Request request); }
1
2
3具体处理类
public class ConcreteHandler1 implements Handler { private Handler nextHandler; @Override public void handleRequest(Request request) { if (request.getType().equals(RequestType.TYPE1)) { System.out.println("ConcreteHandler1 is handling the request..."); // 处理请求 } else if (nextHandler != null) { // 无法处理,传递给下一个处理器 nextHandler.handleRequest(request); } } public void setNextHandler(Handler nextHandler) { this.nextHandler = nextHandler; } } public class ConcreteHandler2 implements Handler { private Handler nextHandler; @Override public void handleRequest(Request request) { if (request.getType().equals(RequestType.TYPE2)) { System.out.println("ConcreteHandler2 is handling the request..."); // 处理请求 } else if (nextHandler != null) { // 无法处理,传递给下一个处理器 nextHandler.handleRequest(request); } } public void setNextHandler(Handler nextHandler) { this.nextHandler = nextHandler; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37请求类
public class Request { private RequestType type; public Request(RequestType type) { this.type = type; } public RequestType getType() { return type; } } public enum RequestType { TYPE1, TYPE2 // 定义两种请求类型 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15客户端
public class Client { public static void main(String[] args) { // 创建责任链 Handler handler1 = new ConcreteHandler1(); Handler handler2 = new ConcreteHandler2(); handler1.setNextHandler(handler2); // 创建请求 Request request1 = new Request(RequestType.TYPE1); Request request2 = new Request(RequestType.TYPE2); // 处理请求 handler1.handleRequest(request1); handler1.handleRequest(request2); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 三、实现方式
声明处理者接口并描述请求处理方法的签名。
确定客户端如何将请求数据传递给方法。 最灵活的方式是将请求转换为对象, 然后将其以参数的形式传递给处理函数。
为了在具体处理者中消除重复的样本代码, 你可以根据处理者接口创建抽象处理者基类。
该类需要有一个成员变量来存储指向链上下个处理者的引用。 你可以将其设置为不可变类。 但如果你打算在运行时对链进行改变, 则需要定义一个设定方法来修改引用成员变量的值。
为了使用方便, 你还可以实现处理方法的默认行为。 如果还有剩余对象, 该方法会将请求传递给下个对象。 具体处理者还能够通过调用父对象的方法来使用这一行为。
依次创建具体处理者子类并实现其处理方法。 每个处理者在接收到请求后都必须做出两个决定:
- 是否自行处理这个请求。
- 是否将该请求沿着链进行传递。
客户端可以自行组装链, 或者从其他对象处获得预先组装好的链。 在后一种情况下, 你必须实现工厂类以根据配置或环境设置来创建链。
客户端可以触发链中的任意处理者, 而不仅仅是第一个。 请求将通过链进行传递, 直至某个处理者拒绝继续传递, 或者请求到达链尾。
由于链的动态性, 客户端需要准备好处理以下情况:
- 链中可能只有单个链接。
- 部分请求可能无法到达链尾。
- 其他请求可能直到链尾都未被处理。