定义
责任链模式旨在解耦发出请求的对象和处理请求的对象。它包括一系列处理对象和一个起始点。当一个请求进入这条链时,它沿着链传递,直到有一个对象能够处理该请求,或者请求到达链的末端。
意图
-
请求者和处理者的解耦: 将发送者与接收者解耦,使得发送者不需要知道请求的具体处理者是谁,以及它们是如何处理请求的。
-
动态创建对象链: 允许在运行时动态地组织对象链和配置处理对象,增加、删除或重新排列处理对象,从而灵活地修改请求的处理方式。
-
分发责任: 每个处理对象只负责自己能够处理的请求,将请求发送给适当的处理者,实现责任的分担和职责的分离。
-
可扩展性和可维护性: 由于责任链模式中的对象链是动态创建的,因此可以方便地扩展和维护。新增处理者或调整处理顺序不需要修改客户端代码。
-
避免请求的无法处理: 确保每个请求都有一个处理者能够处理,如果当前处理者无法处理请求,可以将其传递给下一个处理者,直至请求被处理。
参与者(角色)
-
Handler(处理者): 定义处理请求的接口,并且通常包含一个指向下一个处理者的引用。该接口通常包含一个处理请求的方法。
-
ConcreteHandler(具体处理者): 实现了处理请求的接口。如果自己能够处理请求,则处理它;否则,将请求传递给链中的下一个处理者。
-
Client(客户端): 创建并发送请求给责任链的起始点。客户端并不需要知道请求是由哪个具体的处理者来处理的。
举例
以员工请假,领导审批为例
类图:

这个例子中对应类的角色如下:
Leader类,有名字和下一个领导属性
public abstract class Leader {
protected String name;
protected Leader nextLeader;
public Leader getNextLeader() {
return nextLeader;
}
public void setNextLeader(Leader nextLeader) {
this.nextLeader = nextLeader;
}
public Leader(String name){
this.name =name;
}
public abstract void handleRequest(LeaveRequest request);
}
Manager 类
public class Manager extends Leader {
/**
* @param name
*/
public Manager(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
String name = request.getName();
String reason = request.getReason();
int days = request.getDays();
if (days <= 15) {
System.out.println(name + "需要请假" + days + "天,请假理由" + reason
+ "经理同意审批!");
} else {
System.out.println("超过15天的假期需要董事长审批!");
if (this.nextLeader != null) {
this.nextLeader.handleRequest(request);
}
}
}
}
Boss 具体处理者
public class Boss extends Leader {
/**
* @param name
*/
public Boss(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
String name = request.getName();
String reason = request.getReason();
int days = request.getDays();
if (days <= 30) {
System.out.println(name + "需要请假" + days + "天,请假理由" + reason
+ " 董事长同意审批!");
} else {
System.out.println("我无法处理该请假天数,需要部门商量");
if (this.nextLeader != null) {
this.nextLeader.handleRequest(request);
}else{
System.out.println("暂时不能请假");
}
}
}
}
LeaveRequest,包含请假的参数
public class LeaveRequest {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDays() {
return days;
}
public void setDays(int days) {
this.days = days;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
private String name;
private int days;
private String reason;
}
测试
public static void main(String[] args) {
Leader groupLeader = new GroupLeader("部门组长");
Leader manager = new Manager("部门经理");
Leader boss = new Boss("董事长");
groupLeader.setNextLeader(manager);
manager.setNextLeader(boss);
LeaveRequest leaveRequest = new LeaveRequest();
leaveRequest.setDays(31);
leaveRequest.setName("张三");
leaveRequest.setReason("肚子疼");
groupLeader.handleRequest(leaveRequest);
}
//output
超过5天的假期需要经理审批
超过15天的假期需要董事长审批!
我无法处理该请假天数,需要部门商量
暂时不能请假
优点
-
解耦责任者和请求者: 责任链模式使得请求者和处理者之间解耦,请求者不需要知道具体的处理者,而处理者也不需要知道请求的发送者,降低了对象之间的耦合度。
-
灵活性和可扩展性: 可以动态地增加或修改处理者,改变处理者之间的连接顺序,或者改变处理者的职责,从而灵活地调整和扩展处理流程。
-
单一职责原则: 每个具体处理者都只关注自己负责处理的请求类型,符合单一职责原则,易于维护和修改。
-
降低耦合度: 因为每个处理者只需知道自己的后继者,处理者之间的关系松散,易于独立开发、测试和维护。
-
可以控制处理顺序: 可以通过调整处理者之间的连接顺序来控制请求的处理顺序,可以按照需求定义处理者的优先级。
缺点
-
可能导致请求未被处理: 如果责任链中没有合适的处理者来处理请求,或者所有处理者都没有正确配置,请求可能会到达链的末端而无法被处理。
-
性能问题: 如果责任链过长或者处理者选择不当,可能会导致请求在链中传递过程中出现性能问题。
-
难以调试: 当责任链比较长时,可能会增加代码的复杂性和难以调试的难度。
责任链模式的应用
Netty ChannelPipeline
通过addLast方法构成一个handler处理链
public class ServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
System.out.println(pipeline.toString());
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new FileUploadHandler());
}
}
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
checkMultiplicity(handler);
newCtx = newContext(group, filterName(name, handler), handler);
addLast0(newCtx);
// If the registered is false it means that the channel was not registered on an eventLoop yet.
// In this case we add the context to the pipeline and add a task that will call
// ChannelHandler.handlerAdded(...) once the channel is registered.
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
callHandlerAddedInEventLoop(newCtx, executor);
return this;
}
}
callHandlerAdded0(newCtx);
return this;
}
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}

分享到: