责任链模式

责任链模式

概述

顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

何时使用:在处理消息的时候以过滤很多道。

如何解决:拦截的类都实现统一接口。

关键代码:Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。

应用实例: 1、红楼梦中的”击鼓传花”。 2、JS 中的事件冒泡。 3、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。

优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。

缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。

使用场景: 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。

注意事项:在 JAVA WEB 中遇到很多应用。

结构

职责链模式主要包含以下角色:

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

示例

现需要开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行。

类图

image-20230605161712434

代码

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
public class Request {
private String name;
private int num;
private String content;

public Request(String name, int num, String content) {
this.name = name;
this.num = num;
this.content = content;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getNum() {
return num;
}

public void setNum(int num) {
this.num = num;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}
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
38
39
40
public abstract class Handler {
protected static final int ONE = 1;
protected static final int TWO = 2;
protected static final int THREE = 3;

private int num_start;
private int num_end;
private Handler nextHandler;


public Handler() {
}

public Handler(int num_start) {
this.num_start = num_start;
}

public Handler(int num_start, int num_end) {
this.num_start = num_start;
this.num_end = num_end;
}

public void setNextHandler(Handler nextHandler){
this.nextHandler = nextHandler;
}

public final void submit(Request request){
if (request.getNum() >= this.num_start){
handlerRequest(request);

if(null != this.nextHandler && request.getNum() > num_end){
this.nextHandler.submit(request);
}else {
System.out.println("审批结束");
}
}
}

public abstract void handlerRequest(Request request);
}
1
2
3
4
5
6
7
8
9
10
11
public class Manager01 extends Handler {
public Manager01() {
super(Handler.ONE);
}

@Override
public void handlerRequest(Request request) {
System.out.println("Manager01处理"+ request.getName()+request.getContent());
System.out.println("Manager01同意!!!");
}
}
1
2
3
4
5
6
7
8
9
10
11
public class Manager02 extends Handler{
public Manager02() {
super(Handler.TWO,Handler.THREE);
}

@Override
public void handlerRequest(Request request) {
System.out.println("Manager02处理"+ request.getName()+request.getContent());
System.out.println("Manager02同意!!!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class Manager03 extends Handler{
public Manager03() {
super(Handler.THREE,10);
}


@Override
public void handlerRequest(Request request) {
System.out.println("Manager03处理"+ request.getName()+request.getContent());
System.out.println("Manager03同意!!!");
}
}

测试

image-20230605162014065

优缺点

1,优点:

  • 降低了对象之间的耦合度

    该模式降低了请求发送者和接收者的耦合度。

  • 增强了系统的可扩展性

    可以根据需要增加新的请求处理类,满足开闭原则。

  • 增强了给对象指派职责的灵活性

    当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。

  • 责任链简化了对象之间的连接

    一个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。

  • 责任分担

    每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

2,缺点:

  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

源码解析

在javaWeb应用开发中,FilterChain是职责链(过滤器)模式的典型应用,以下是Filter的模拟实现分析:

  • 模拟web请求Request以及web响应Response

    1
    2
    3
    4
    5
    6
    7
    public interface Request{

    }

    public interface Response{

    }
  • 模拟web过滤器Filter

    1
    2
    3
    public interface Filter {
    public void doFilter(Request req,Response res,FilterChain c);
    }
  • 模拟实现具体过滤器

    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
    public class FirstFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {

    System.out.println("过滤器1 前置处理");

    // 先执行所有request再倒序执行所有response
    chain.doFilter(request, response);

    System.out.println("过滤器1 后置处理");
    }
    }

    public class SecondFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {

    System.out.println("过滤器2 前置处理");

    // 先执行所有request再倒序执行所有response
    chain.doFilter(request, response);

    System.out.println("过滤器2 后置处理");
    }
    }
  • 模拟实现过滤器链FilterChain

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class FilterChain {

    private List<Filter> filters = new ArrayList<Filter>();

    private int index = 0;

    // 链式调用
    public FilterChain addFilter(Filter filter) {
    this.filters.add(filter);
    return this;
    }

    public void doFilter(Request request, Response response) {
    if (index == filters.size()) {
    return;
    }
    Filter filter = filters.get(index);
    index++;
    filter.doFilter(request, response, this);
    }
    }
  • 测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Client {
    public static void main(String[] args) {
    Request req = null;
    Response res = null ;

    FilterChain filterChain = new FilterChain();
    filterChain.addFilter(new FirstFilter()).addFilter(new SecondFilter());
    filterChain.doFilter(req,res);
    }
    }