责任链模式
定义
责任链(Chain of Responsibility) 模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
责任链模式包含了一些命令对象和一些处理对象,每个处理对象决定它能处理那些命令对象,并把自己不能处理的命令对象交下一个处理对象,同时可以在该链中扩展新的处理对象方法。责任链模式也叫职责链模式。
应用场景
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可动态指定一组对象处理请求。
代码讲解
老规矩我们设想一个场景:
我们模拟一个手机的生产到上市的生命周期过程,这里简单起见我们直接用字符串不断添加的形式来模拟,phone
从空由shape
添加外形,由hardware
添加硬件,由software
添加系统和软件,由pick
进行包装后由sell
来出售:
public class Main {
public static void main(String[] args) {
String phone = "null";
String shape = "addShape";//外形
phone = phone + " -> " + shape;
String hardware = "addHardware";//硬件
phone = phone + " -> " + hardware;
String software = "addSystem & Software";//系统和软件
phone = phone + " -> " + software;
String pick = "addPick";//包装
phone = phone + " -> " + pick;
String sell = "sellPhone";//出售
phone = phone + " -> " + sell;
System.out.println(phone);
}
}
运行结果:
通过上述代码我们可以看出,在phone
的整个生命周期中,其中每个参与者都负责着不同的功能,程序若是想在此基础上对phone
进行扩展就变得十分麻烦。下面使用责任链模式进行改写:
- 首先写一个接口
Responsibility
,他有一个toAdd()
方法负责传phone
的值:
interface Responsibility{
String toAdd(String phone);
}
2.把phone
生命周期过程的参与者都写出单独的类,都实现Responsibility
并根据自己的职责重写toAdd()
方法,比如:
class Shape implements Responsibility{
public String toAdd(String phone) {
String shape = "addShape";//外形
phone = phone + " -> " + shape;
return phone;
}
}
- 然后我们的主程序就可以改写为:
public class Main {
public static void main(String[] args) {
String phone = "null";
phone = new Shape().toAdd(phone);
phone = new Hardware().toAdd(phone);
phone = new Software().toAdd(phone);
phone = new Pick().toAdd(phone);
phone = new Sell().toAdd(phone);
System.out.println(phone);
}
}
运行结果:
当然到现在这还没有做成责任“链”模式,我们接下来添加一个Responsibility
类型的List
,并把每个责任对象存入其中,再通过foreach
调用每个责任对象的toAdd()
方法为phone
添加内容:
public static void main(String[] args) {
String phone = "null";
List<Responsibility> list = new ArrayList<Responsibility>();
list.add(new Shape());
list.add(new Hardware());
list.add(new Software());
list.add(new Pick());
list.add(new Sell());
for (Responsibility r : list) {
phone = r.toAdd(phone);
}
System.out.println(phone);
}
运行结果:
通过上述方式我们把每个责任对象连在一起,并可以随意扩展或删减责任对象。这就已经是一个简单的责任链模式了,但他并不是完整。接下来我们就把这个程序写成一个完整的责任链模式:
- 我们写一个
PhoneChain
类来存放这条生产线List
,同样实现Responsibility
,他有add()
方法来添加责任对象,重写toAdd()
方法来让每个参与者给phone
添加内容:
class PhoneChain implements Responsibility{
List<Responsibility> list = new ArrayList<Responsibility>();
public void add(Responsibility r) {
list.add(r);
}
public String toAdd(String phone) {
for (Responsibility r : list) {
phone = r.toAdd(phone);
}
return phone;
}
}
- 主程序就可以改写为:
public class Main {
public static void main(String[] args) {
String phone = "null";
PhoneChain chain = new PhoneChain();
chain.add(new Shape());
chain.add(new Hardware());
chain.add(new Software());
chain.add(new Pick());
chain.add(new Sell());
phone = chain.toAdd(phone);
System.out.println(phone);
}
}
运行结果:
这里我们的责任链就完整写完了。通过上述改写,大家可能又会有疑问:这个完成版的责任链和之前简单版的责任链有什么不同?不过是一个直接把责任对象写入List
,而另一个是专门加了一个Chain
来负责写入List
和读取吗?
其实区别还是很大的,不论是语义上还是开发逻辑上都有很大不同。比如我们对代码再进一步改写:
- 我们把
PhoneChain
中的add()
方法返回值改为PhoneChain
:
public PhoneChain add(Responsibility r) {
list.add(r);
return this;
}
- 我们的主程序就可以一步改写为:
public static void main(String[] args) {
String phone = "null";
PhoneChain chain = new PhoneChain();
phone = chain.add(new Shape())
.add(new Hardware())
.add(new Software())
.add(new Pick())
.add(new Sell())
.toAdd(phone);
System.out.println(phone);
}
到此我们才真正意义上的完成了责任链模式的链式编程这样写是我们的程序更加灵活扩展,比如在添加一个生产线PhoneChain2
,可以让他有不同的生产步骤,但只需要使用chain.add(chain2)
就可以让他们连在一起。
扩展
中断
通过上面的代码讲解知道我们的程序是以一条链向下进行的。但实际需求中有些情况是当程序中某个责任部分处理完之后就结束了不会继续向下执行了。
比如我们的手机生产线中添加一个质量检测的责任对象,在他发现手机质量不过关的时候就不会让它继续拿来卖了,这种情况我们该怎么处理呢?
- 我们对上面的例子进行改写。首先把
Phone
单独出来,并写出他的get
和set
用于处理:
class Phone{
String phone = "null";
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
程序中的phone
都改为Phone
类型,通过getPhone()
取值,setPhone()
赋值。
- 将
Responsibility
中toAdd()
方法改为boolean
类型,并改写每个实现他的类中重写的方法类型如:
interface Responsibility{
boolean toAdd(Phone phone);
}
class Shape implements Responsibility{
public boolean toAdd(Phone phone) {
String shape = "addShape";//外形
phone.setPhone(phone.getPhone() + " -> " + shape);
return true;
}
}
- 改写
PhoneChain
中的toAdd()
方法,使其根据返回值进行判断是否是链条继续:
public boolean toAdd(Phone phone) {
for (Responsibility r : list) {
if (!r.toAdd(phone)) {
return false;
}
}
return true;
}
- 我们添加一个质检对象
Check
,使其判断产品质量不达标,返回false
:
class Check implements Responsibility{
public boolean toAdd(Phone phone) {
String check = "checkPhone";//质检
phone.setPhone(phone.getPhone() + " -> " + check);
return false;
}
}
- 主程序中把质检对象加入到责任链中:
public class Main {
public static void main(String[] args) {
Phone phone = new Phone();
PhoneChain chain = new PhoneChain();
chain.add(new Shape())
.add(new Hardware())
.add(new Software())
.add(new Check())
.add(new Pick())
.add(new Sell());
chain.toAdd(phone);
System.out.println(phone.getPhone());
}
}
运行结果:
因为质量不达标,质检责任对象将链条中断了,所以后面的包装和出售就都不进行了。
拦截器
在JAVA SE的官方文档中有对FilterChain
的相关介绍,他使用到的是一种Request & Response
的责任链模式。责任链中的每个责任对象首先接收请求,根据请求进行判断和处理,然后继续向后面的责任对象发送请求或是返回响应内容。
代码模拟:
- 我们还是以上面的代码改写,首先添加请求
Request
类和响应Response
类,他们都有自己的封装的内容。这里还是用字符串举例:
class Request {
String request;
}
class Response {
String response;
}
- 将接口的方法参数改为
Request
和Response
类型,为简单讲解我们就只留两个责任对象,并将方法内容改为分别为request
和response
添加内容,如:
interface Responsibility{
boolean toAdd(Request request, Response response);
}
class Create implements Responsibility{
public boolean toAdd(Request request, Response response) {
request.request = request.request + " -> created!";
response.response = response.response + " ->Create ";
return true;
}
}
class Sell implements Responsibility{
public boolean toAdd(Request request, Response response) {
request.request = request.request + " -> selled!";
response.response = response.response + "->Sell";
return true;
}
}
- 主方法改写为:
public static void main(String[] args) {
Phone phone = new Phone();
Request request = new Request();
request.request = phone.getPhone();
Response response = new Response();
response.response = "response";
PhoneChain pc = new PhoneChain();
pc.add(new Create()).add(new Sell());
pc.toAdd(request, response);
System.out.println(request.request);
System.out.println(response.response);
}
运行结果:
通过运行结果我们可以看出来,request
和response
是同步进行的,而并不是先由请求一直向链条最后发送,再返回处理响应。若是想做成这种效果,那我们还需将代码修改:
- 首先改写
PhoneChain
类,定义一个整数i
来记录chain
的位置,这里可以将其理解为责任链中的指针。改写toAdd()
方法,使用递归的思路首先由i
判断是否已经指向责任链的最后一位来控制返回,责任链中每向后执行一次就让指针i
+1:
public boolean toAdd(Request request, Response response, PhoneChain chain) {
if (i == list.size()) {
return false;
}
Responsibility r = list.get(i);
i ++;
return r.toAdd(request, response, chain);
}
- 我们将每个责任对象中方法的参数再加一个
PhoneChain
,并在方法体内在response
之前调用chain.toAdd()
,如;
public boolean toAdd(Request request, Response response, PhoneChain chain) {
request.request = request.request + " -> created!";
chain.toAdd(request, response, chain);
response.response = response.response + " ->Create ";
return true;
}
- 同样我们在主方法中也把
pc
的参数改为:
pc.toAdd(request, response, pc);
运行结果:由此可见我们的处理过程反过来了。
其实在javase官方文档中有这种责任链的详细介绍,可以参考java.lang.servet JPI中:servlet FilterChain