责任链模式定义如下:使多个对象都有机会处理请求,从而避免请求的发送者与接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止。
适合使用责任链模式的情景如下:
在生活中我们经常会遇到这样的问题,例如:在企业工作的员工请假问题。假设假期少于一天的可由组长决定;多余1天少于2天的,可由车间主任决定;大于2天的可由经理决定。 “组长——主任——经理”构成了一个功能链。员工提出请假请求后,组长根据权限决定是否同意员工请假。若超出其权限,则传递给下一个责任人——主任。主任根据权限决定是否同意请假,若超出权限,在传递给下一个责任人——经理。
也就是说,请假这个“请求”,一定可以在功能链中的某一节点处理并返回。
由于是一系列类试图处理一个请求request,这些类之间是一个松散的耦合,唯一的共同点就是在他们之间传递请求request。
也就是说,来了一个请求,A类先处理,如果没有处理,就传递到B类处理,如果没有处理,就传递给C类处理,这就像一个链条一样传递下去。
责任链模式涉及的角色如下:
- 抽象处理者角色(Handler):定义一个处理请求的接口或抽象类。可定义一个方法,以设定和返回对下一节点的引用。
- 具体处理者(ConcreteHandler):具体处理者接到请求后,可以选择将请求处理完毕,或者将请求传给下一节点。由于具体处理者持有对下一节点的引用,因此,如果需要,具体处理者可以访问下一节点。
- 客户(Client):负责形成具体处理者的节点功能链,并传递初始请求。
利用责任链模式编制员工请假审批功能类:
(1)请求类
public class Request {
int day; //请假的天数
Request(int day) {
this.day = day;
}
}
(2)抽象处理者类Handler
public abstract class Handler {
private Handler next;
public Handler getNext() {
return next;
}
public void setNext(Handler next) {
this.next = next;
}
//定义抽象请求方法,子类要重写
public abstract boolean handle(Request request);
}
(3)三个具体处理者类
public class ZuZhang extends Handler{
static int limit = 1;
@Override
public boolean handle(Request request) {
if (request.day <= limit) {
System.out.println("组长同意请假!");
return true;
}
return getNext().handle(request);
}
}
public class ZhuRen extends Handler{
static int limit = 2;
@Override
public boolean handle(Request request) {
if (request.day <= limit) {
System.out.println("主任同意请假!");
return true;
}
return getNext().handle(request);
}
}
public class JingLi extends Handler{
static int limit = 3;
@Override
public boolean handle(Request request) {
if (request.day <= limit) {
System.out.println("经理同意请假!");
return true;
}
return getNext().handle(request);
}
}
(4)生成责任链前后顺序关系类
public class MyChain {
private Handler one = new ZuZhang();
private Handler two = new ZhuRen();
private Handler three = new JingLi();
//生成责任链
public void createChain() {
one.setNext(two);
two.setNext(three);
}
public void handle(Request request) {
one.handle(request);
}
}
(5)测试类
public class Test {
public static void main(String[] args) {
Request request = new Request(3);
MyChain myChain = new MyChain();
myChain.createChain();
myChain.handle(request);
}
}
测试结果:
经理同意请假!
在上面的MyChain类可以看出:所形成的责任链是刚性的,若需求分析发生了变化,链中需增加或减少节点,我们必须重新修改MyChain类,以适应需求分析发展的需要。那么,能否不修改程序,而又能满足需求分析的变化呢?答案是可以的,那就是“配置文件+反射”技术。
(1)对上面的例子而言,编写myconfig.txt配置文件:
chain=shejimoshi.zerenlian.ZuZhang,shejimoshi.zerenlian.ZhuRen,shejimoshi.zerenlian.JingLi
放在了这个位置:
(2)创建MyChain2
注释中很详细了写了每行代码的含义
import java.io.FileInputStream;
import java.util.Properties;
public class MyChain2 {
//声明了一个私有成员变量 handle,用于存储责任链中的各个节点。
private Handler handle[];
public void createChain() {
try {
//这里打开了一个配置文件 myconfig.txt,并使用 Properties 类加载其中的配置信息。
FileInputStream in = new FileInputStream("D:\\Java_Dev\\IDEA_Projects\\Personal_project\\programmer_code\\src\\main\\java\\shejimoshi\\zerenlian\\myconfig.txt");
Properties properties = new Properties();
properties.load(in);
///获取了配置文件中 chain 属性的值,并按照逗号分隔成一个字符串数组 unit,表示责任链中的节点。
// 然后,代码通过数组的长度创建了一个 Handler 类型的数组 handle,用于存储责任链中的各个节点。
String chain = properties.getProperty("chain");
String unit[] = chain.split(",");
int chainLength = unit.length;
handle = new Handler[chainLength];
//通使用Java反射机制,根据 unit 数组中的类名动态加载各个责任链节点对象,并存储到 handle 数组中。
for (int i = 0; i < chainLength; i++) {
handle[i] = (Handler) Class.forName(unit[i]).newInstance();
}
//通过遍历 handle 数组,设置责任链节点之间的前后关系。
for (int i = 0; i < chainLength - 1; i++) {
handle[i].setNext(handle[i+1]);
}
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//这个方法是责任链模式的核心,用于处理请求。这里直接调用责任链中的第一个节点来处理请求。
public void handle(Request request) {
handle[0].handle(request);
}
}
(3)再次测试
public class Test {
public static void main(String[] args) {
Request request = new Request(2);
MyChain2 myChain = new MyChain2();
myChain.createChain();
myChain.handle(request);
}
}
测试结果:
主任同意请假!
(4)新增责任链
增加一个董事长的处理者,经理不能处理的时候转交给董事长处理
public class DongShiZhang extends Handler{
static int limit = 5;
@Override
public boolean handle(Request request) {
if (request.day <= limit) {
System.out.println("董事长同意请假!");
return true;
}
return getNext().handle(request);
}
}
(5)修改配置文件
增加董事长
chain=shejimoshi.zerenlian.ZuZhang,shejimoshi.zerenlian.ZhuRen,shejimoshi.zerenlian.JingLi,shejimoshi.zerenlian.DongShiZhang
(5)再次测试
public class Test {
public static void main(String[] args) {
Request request = new Request(4);
MyChain2 myChain = new MyChain2();
myChain.createChain();
myChain.handle(request);
}
}
测试结果:
董事长同意请假!
总结:可以看到,当需求发生变化时,只需要增加责任链中的具体处理者类,然后在配置文件中添加该具体处理者,并不需要去修改MyChain2的内容,这就是反射的作用。