咸鱼开发修炼之路

设计模式-责任链模式

责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
38bbbc1f.png

责任链模式的要点

  1. 有多个对象共同对一个任务进行处理。

  2. 这些对象使用链式存储结构,形成一个链,每个对象知道自己的下一个对象。

  3. 一个对象对任务进行处理,可以添加一些操作后将对象传递个下一个任务。也可以在此对象上结束任务的处理,并结束任务。

  4. 客户端负责组装链式结构,但是客户端不需要关心最终是谁来处理了任务。

涉及角色

  • 抽象处理者角色(Handler:Approver):定义一个处理请求的接口,和一个后继连接(可选)
  • 具体处理者角色(ConcreteHandler:President):处理它所负责的请求,可以访问后继者,如果可以处理请求则处理,否则将该请求转给他的后继者。
  • 客户类(Client):向一个链上的具体处理者ConcreteHandler对象提交请求。

实现1 Handler

一个简单的处理数字的demo:
抽象处理者Handler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class Handler {
private Handler nextHandle;
public Handler setNextHandle(Handler handler){
this.nextHandle = handler;
return this.nextHandle;
}
public Handler getNextHandle() {
return nextHandle;
}
public abstract void handle(int number);
}

具体处理者角色1:处理大于10的数
1
2
3
4
5
6
7
8
9
10
11
12
public class BigNumberHandler extends Handler {
@Override
public void handle(int number) {
if (number > 10) {
System.out.println("BigNumberHandler deal this number :" + number);
} else { //如果不是本处理器处理的内容则调用下一个处理器
if (this.getNextHandle() != null) {
this.getNextHandle().handle(number);
}
}
}
}

具体处理者角色2:处理0-10之间的数
1
2
3
4
5
6
7
8
9
10
11
12
public class SmallNumberHandler extends Handler {
@Override
public void handle(int number) {
if (number < 10 && number >= 0) {
System.out.println("SmallNumberHandler deal this number:" + number);
} else { //如果不是本处理器处理的内容则调用下一个处理器
if (this.getNextHandle() != null) {
this.getNextHandle().handle(number);
}
}
}
}

具体处理者角色3:用于处理负数
1
2
3
4
5
6
7
8
9
10
11
12
public class NegativeHandler extends Handler {
@Override
public void handle(int number) {
if(number < 0){
System.out.println("NegativeHandler deal this number:"+number);
}else { //如果不是本处理器处理的内容则调用下一个处理器
if(this.getNextHandle() != null){
this.getNextHandle().handle(number);
}
}
}
}

客户类(测试类):
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test {
public static void main(String[] args) {
Handler bigNumberHandler = new BigNumberHandler();
Handler smallNumberHandler = new SmallNumberHandler();
Handler NegativeHandler = new NegativeHandler();
int[] numbers = {1, 20, 13, -10, 24, 5, -14};
bigNumberHandler.setNextHandle(smallNumberHandler).setNextHandle(NegativeHandler);
for (int num : numbers) {
bigNumberHandler.handle(num);
}
}

运行结果:

1
2
3
4
5
6
7
8
9
SmallNumberHandler deal this number:1
BigNumberHandler deal this number :20
BigNumberHandler deal this number :13
NegativeHandler deal this number:-10
BigNumberHandler deal this number :24
SmallNumberHandler deal this number:5
NegativeHandler deal this number:-14
Process finished with exit code 0

实现2 Filter

Sevlet中的filter在web开发中可以对web请求做各种处理和过滤,包括:对请求和相应的字符集处理、对跨站脚本攻击的过滤、获取客户端真实ip地址等
f3748c81.png
过滤器接口:

1
2
3
public interface Filter {
void doFilter(String request ,String response,FilterChain filterChain);
}

过滤器链:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class FilterChain {
private List<Filter> filters = new ArrayList<>();
private int cursor =0 ;
public FilterChain addFilter(Filter filter) {
this.filters.add(filter);
return this;
}
public void doFilter(String request ,String response) {
if (cursor < filters.size()) {
filters.get(cursor++).doFilter(request,response, this);
}
}
}

实现两个过滤器:
1
2
3
4
5
6
7
8
9
public class UpperCaseFilter implements Filter {
@Override
public void doFilter(String request ,String response,FilterChain filterChain) {
System.out.println(request.toUpperCase());
filterChain.doFilter(request,response);
System.out.println(response.toUpperCase());
}
}

1
2
3
4
5
6
7
8
9
public class AddTimeFilter implements Filter {
@Override
public void doFilter(String request, String response, FilterChain filterChain) {
String datetime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
System.out.println("request:[" + datetime + "]" + request);
filterChain.doFilter(request, response);
System.out.println("response:[" + datetime + "]" + response);
}
}

测试:
1
2
3
4
5
6
public class Test {
public static void main(String[] args){
FilterChain filterChain = new FilterChain();
filterChain.addFilter(new UpperCaseFilter()).addFilter(new AddTimeFilter()).doFilter("deal request" ,"deal response");
}
}

运行结果:
1
2
3
4
5
6
DEAL REQUEST
request:[2017-12-31 14:32:00]deal request
response:[2017-12-31 14:32:00]deal response
DEAL RESPONSE
Process finished with exit code 0

使用场景

在工作中,尤其是java web开发中,有两个地方明显使用责任链模式,一个是filter,一个是listener,filter的自定义在web开发中可以对web请求做各种处理和过滤,包括:对请求和相应的字符集处理、对跨站脚本攻击的过滤、获取客户端真实ip地址、获取客户证书、防止盗链等等.

filter的责任链实现与责任链模式的标准代码有着一定的差距,它具有如下特点:

  1. 责任链的实现并不是链式结构,而是以一个FilterChain保存了所有责任链的引用,通过FilterChain的doFilter方法依次调用filter进行执行;

  2. filter中同时也保存了FilterChain的引用,形成了一个双向引用;

  3. FilterChain将加入容器中的filters按照顺序进行调用执行

由此,责任链模式的实现应该有多种形式,可以为责任链之间的互相链式引用,也可以为第三方集合中的顺序执行方式