基本定义
- 为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
- 在这里客户端和处理者都没有对方明确的信息,同时处理者也不知道职责链中的结构。所以职责链可以简化对象的相互连接,他们只需要保存一个指向其后续者的引用,而不需要保存所有候选者的引用。
- 在职责链模式中我们可以随时随地的增加或者更改一个处理者,甚至可以更改处理者的顺序,增加了系统的灵活性。
模式结构
- Handler: 抽象处理者。定义了一个处理请求的方法。所有的处理者都必须实现该抽象类。
- ConcreteHandler: 具体处理者。处理它所负责的请求,同时也可以访问它的后继者。如果它能够处理该请求则处理,否则将请求传递到它的后继者。
- Client:客户端
模式实现
- Handler:抽象处理者
public abstract class Leader {
private String position;//岗位
Leader nextLeader;//下一节点审核人员
public Leader(String position) {
super();
this.position = position;
}
public void setNextLeader(Leader nextLeader) {
this.nextLeader = nextLeader;
}
//处理请求
public abstract void process(LeaveRequest leaveRequest);
}
- ConcreteHandler:具体处理者
组长 批准1天及以下假期, 职责链上的一个具体处理者
@Slf4j
public class GroupLeader extends Leader {
public GroupLeader(String position) {
super(position);
}
@Override
public void process(LeaveRequest leaveRequest) {
if(leaveRequest.getDays() <= 1){
log.info("组长批准: 同意。【请假单:姓名{}, 请假天数: {} , 原因:{} 】",
leaveRequest.getName(),
leaveRequest.getDays() ,
leaveRequest.getReason());
}else {
if(nextLeader == null){
log.info("该请假单不能被批准:{} ", leaveRequest);
}
nextLeader.process(leaveRequest);
}
}
}
经理-批准3天及以下, 职责链上的一个具体处理者
@Slf4j
public class ManageLeader extends Leader{
public ManageLeader(String position) {
super(position);
}
@Override
public void process(LeaveRequest leaveRequest) {
if(leaveRequest.getDays() <= 3){
log.info("经理批准: 同意。【请假单:姓名{}, 请假天数: {} , 原因:{} 】",
leaveRequest.getName(),
leaveRequest.getDays() ,
leaveRequest.getReason());
}else{
if(nextLeader == null){
log.info("该请假单不能被批准:{} ", leaveRequest);
return;
}
nextLeader.process(leaveRequest);
}
}
}
CEO-批准5天及以下,职责链上的一个具体处理者
@Slf4j
public class CEOLeader extends Leader{
public CEOLeader(String position) {
super(position);
}
@Override
public void process(LeaveRequest leaveRequest) {
if(leaveRequest.getDays() <= 5){
log.info("CEO批准: 同意。【请假单:姓名{}, 请假天数: {} , 原因:{} 】",
leaveRequest.getName(),
leaveRequest.getDays() ,
leaveRequest.getReason());
}else{
if(nextLeader == null){
log.info("该请假单不能被批准:{} ", leaveRequest);
return;
}
nextLeader.process(leaveRequest);
}
}
}
- POJO: 普通Java类
@Getter
@Setter
public class LeaveRequest {
private String name;
private int days;
private String reason;
public LeaveRequest() {
}
public LeaveRequest(String name, int days, String reason) {
this.name = name;
this.days = days;
this.reason = reason;
}
@Override
public String toString() {
return "LeaveRequest{" +
"name='" + name + '\'' +
", days=" + days +
", reason='" + reason + '\'' +
'}';
}
}
- Client: 测试类
public class Test {
public static void main(String[] args){
Leader groupLeader = new GroupLeader("组长");
Leader manageLeader = new ManageLeader("经理");
groupLeader.setNextLeader(manageLeader);
Leader ceo = new CEOLeader("总经理");
manageLeader.setNextLeader(ceo);
LeaveRequest oneDay = new LeaveRequest("林冲", 1, "爬山");
LeaveRequest threeDays = new LeaveRequest("吴用", 3, "回家相亲");
LeaveRequest fiveDays = new LeaveRequest("武松", 5, "去打猎");
LeaveRequest sixDays = new LeaveRequest("李逵", 7, "去旅行");
groupLeader.process(oneDay);
groupLeader.process(threeDays);
groupLeader.process(fiveDays);
groupLeader.process(sixDays);
}
}
输出结果
组长批准: 同意。【请假单:姓名林冲, 请假天数: 1 , 原因:爬山 】
经理批准: 同意。【请假单:姓名吴用, 请假天数: 3 , 原因:回家相亲 】
CEO批准: 同意。【请假单:姓名武松, 请假天数: 5 , 原因:去打猎 】
该请假单不能被批准:LeaveRequest{name=‘李逵’, days=7, reason=‘去旅行’}
优点
- 降低耦合度。它将请求的发送者和接受者解耦。
- 简化了对象。使得对象不需要知道链的结构。
- 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
缺点
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
使用场景
- 有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
- 可动态指定一组对象处理请求,或添加新的处理者。
- 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
总结
- 责链模式将请求的发送者和接受者解耦了。客户端不需要知道请求处理者的明确信息,甚至不需要知道链的结构,它只需要将请求进行发送即可。
- 处理者不需要知道链的结构,只需要明白他的后续者是谁就可以了。这样就简化了系统中的对象。
- 职责链模式能够非常方便的动态增加新职责或者删除职责。
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
JDK源码
- javax.servlet.Filter
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
}
org.apache.catalina.filters.FilterBase
public abstract class FilterBase implements Filter{
}
org.apache.catalina.filters.ExpiresFilter 设置静态文件缓存
public class ExpiresFilter extends FilterBase {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest &&
response instanceof HttpServletResponse) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (response.isCommitted()) {
if (log.isDebugEnabled()) {
log.debug(sm.getString(
"expiresFilter.responseAlreadyCommited",
httpRequest.getRequestURL()));
}
chain.doFilter(request, response);
} else {
XHttpServletResponse xResponse = new XHttpServletResponse(
httpRequest, httpResponse);
chain.doFilter(request, xResponse);
if (!xResponse.isWriteResponseBodyStarted()) {
// Empty response, manually trigger
// onBeforeWriteResponseBody()
onBeforeWriteResponseBody(httpRequest, xResponse);
}
}
} else {
chain.doFilter(request, response);
}
}
}