在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据自己要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。这样的例子还有很多,如找领导出差报销、生活中的“击鼓传花”游戏等。
在计算机软硬件中也有相关例子,如总线网中数据报传送,每台计算机根据目标地址是否同自己的地址相同来决定是否接收;还有异常处理中,处理程序根据异常的类型决定自己是否处理该异常;还有 Struts2 的拦截器、JSP 和 Servlet 的 Filter 等,所有这些,如果用责任链模式都能很好解决。
什么是职责链(Chain of Responsibility)模式?
责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
责任链模式是一种对象行为型模式,其主要优点如下。
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
- 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
其主要缺点如下。
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
例子
在这个例子中,我们将请求处理程序({@link RequestHandler})组织成一个链,其中每个处理程序都有机会对请求执行相应的操作。这里是国王({@link orking})提出请求和军事兽人({@link OrcCommander},{@link orccomator},{@link OrcOfficer},{@link)(兽人士兵})形成处理链。
public class App {
/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
var king = new OrcKing();
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle(保卫城堡)"));
king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner(折磨犯人)"));
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax(征收税款)"));
}
}
/**
* RequestHandler.
*/
public abstract class RequestHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RequestHandler.class);
private final RequestHandler next;
public RequestHandler(RequestHandler next) {
this.next = next;
}
/**
* Request handler.
*/
public void handleRequest(Request req) {
if (next != null) {
next.handleRequest(req);
}
}
protected void printHandling(Request req) {
LOGGER.info("{} handling request(处理请求) \"{}\"", this, req);
}
@Override
public abstract String toString();
}
/**
* Request.
*/
public class Request {
/**
* The type of this request, used by each item in the chain to see if they should or can handle
* this particular request.
*/
private final RequestType requestType;
/**
* A description of the request.
*/
private final String requestDescription;
/**
* Indicates if the request is handled or not. A request can only switch state from unhandled to
* handled, there's no way to 'unhandle' a request.
*/
private boolean handled;
/**
* Create a new request of the given type and accompanied description.
*
* @param requestType The type of request
* @param requestDescription The description of the request
*/
public Request(final RequestType requestType, final String requestDescription) {
this.requestType = Objects.requireNonNull(requestType);
this.requestDescription = Objects.requireNonNull(requestDescription);
}
/**
* Get a description of the request.
*
* @return A human readable description of the request
*/
public String getRequestDescription() {
return requestDescription;
}
/**
* Get the type of this request, used by each person in the chain of command to see if they should
* or can handle this particular request.
*
* @return The request type
*/
public RequestType getRequestType() {
return requestType;
}
/**
* Mark the request as handled.
*/
public void markHandled() {
this.handled = true;
}
/**
* Indicates if this request is handled or not.
*
* @return <tt>true</tt> when the request is handled, <tt>false</tt> if not
*/
public boolean isHandled() {
return this.handled;
}
@Override
public String toString() {
return getRequestDescription();
}
}
/**
* RequestType enumeration.
*/
public enum RequestType {
DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX
}
/**
* OrcKing makes requests that are handled by the chain.
*/
public class OrcKing {
private RequestHandler chain;
public OrcKing() {
buildChain();
}
private void buildChain() {
chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null)));
}
public void makeRequest(Request req) {
chain.handleRequest(req);
}
}
/**
* OrcCommander.
*/
public class OrcCommander extends RequestHandler {
public OrcCommander(RequestHandler handler) {
super(handler);
}
@Override
public void handleRequest(Request req) {
if (RequestType.DEFEND_CASTLE == req.getRequestType()) {
printHandling(req);
req.markHandled();
} else {
super.handleRequest(req);
}
}
@Override
public String toString() {
return "Orc commander(兽人指挥官)";
}
}
/**
* OrcOfficer.
*/
public class OrcOfficer extends RequestHandler {
public OrcOfficer(RequestHandler handler) {
super(handler);
}
@Override
public void handleRequest(Request req) {
if (RequestType.TORTURE_PRISONER == req.getRequestType()) {
printHandling(req);
req.markHandled();
} else {
super.handleRequest(req);
}
}
@Override
public String toString() {
return "Orc officer(兽人军官)";
}
}
/**
* OrcSoldier.
*/
public class OrcSoldier extends RequestHandler {
public OrcSoldier(RequestHandler handler) {
super(handler);
}
@Override
public void handleRequest(Request req) {
if (RequestType.COLLECT_TAX == req.getRequestType()) {
printHandling(req);
req.markHandled();
} else {
super.handleRequest(req);
}
}
@Override
public String toString() {
return "Orc soldier(兽人士兵)";
}
}