好用的设计模式 -- 责任链
责任链是一种日常开发常用的设计模式,这里之所以说它好用,是因为责任链的形式很贴合面向过程的开发思路,易于理解,同时链式也利于开发者归纳功能,管理代码。
其实这种设计模式很容易理解,我们都知道链表(单向)的数据结构:
class Node {
private Node next;
private Object value;
……(构造方法)
}
理解链表就很容易理解责任链:责任链是由多个执行对象构成的,它们都有两个共同的属性:一是“next”,表示下一个执行对象;二是执行方法,也就是说当前对象需要进行的操作方法。既然所有执行对象都有这两个共同的属性,我们就可以抽象出一个抽象类,它有一个基础属性“next”,和抽象方法:“handleWork”:
public abstract class Handler {
protected Handler next;
abstract protected String handleWork(Object obj);
}
看起来Node和Handler可以对应上,只是说Node没有具体的行为方法,而Handler有基础方法。
有以上的基础知识,咱们来实现这样一个场景:
咱们有一个收入证明需要申请公司盖章,这个过程中需要小组领导,部门主管,公司财务进行审批。只有顺次通过之后,才可以最终获得公章。这里我们可以按照责任链的思路进行实现:
1. 创建证明的抽象类 Certification.java,虽然是个示例,但是我们还是按照真实设计出发,证明的类型可能有很多,比如收入证明,结婚证明,核算证明等等,咱们抽象出一个证明类,然后创建它的子类IncomeCertification.java。
public abstract class Certification {
protected String name;
/**
* 1 - 收入证明
* 2 - 核算证明
* 3 - 结婚证明
*/
private Integer type;
private String owner;
//... get and set methods
}
public class IncomeCertification extends Certification {
private Integer income;
//... get and set methods
}
ok!entity定义完成,可以看到,收入证明有4个属性,其中名称、类型、拥有者是继承自父类,具体收入是个性化属性。现在咱们定义责任链执行对象的基类:
public abstract class Handler {
protected Handler next;
abstract protected boolean processCertification(Certification cert);
}
对于小组领导来说,他需要确认该申请人属于自己团队:
public class GroupLeaderHandler extends Handler {
private static List<String> teamMember = new ArrayList<String>();
static {
teamMember.add("ruiruiyuzhi");
teamMember.add("zhangsan");
}
@Override
protected boolean processCertification(Certification cert) {
if (teamMember.contains(cert.getOwner())) {
return this.next.processCertification(cert);
}
return false;
}
}
对于部门领导来说,他需要确认收入无误:
public class DepartmentLeaderHandler extends Handler {
private static final Integer INCOME_TYPE = 1;
private static final Integer INCOME = 1000;
@Override
protected boolean processCertification(Certification cert) {
IncomeCertification incomeCert = (IncomeCertification) cert;
if (INCOME_TYPE.equals(cert.getType()) && INCOME.equals(incomeCert.getIncome())) {
return this.next.processCertification(cert);
}
return false;
}
}
财务人员判断申请类型为已有类型,则审批通过:
public class FinancialHandler extends Handler {
private static List<Integer> certType = new ArrayList<Integer>();
static {
certType.add(1);
certType.add(2);
certType.add(3);
}
@Override
protected boolean processCertification(Certification cert) {
return certType.contains(cert.getType());
}
}
到目前为止我们准备好了各个责任链环节的执行对象,进而需要将其串成链,这个过程跟链表的初始化相似:
public static void main(String[] args) {
//1.初始化责任链
GroupLeaderHandler groupLeaderHandler = new GroupLeaderHandler();
DepartmentLeaderHandler departmentLeaderHandler = new DepartmentLeaderHandler();
FinancialHandler financialHandler = new FinancialHandler();
groupLeaderHandler.setNext(departmentLeaderHandler);
departmentLeaderHandler.setNext(financialHandler);
……
其次,构建待审批的证明对象:
public static void main(String[] args) {
//1.初始化责任链
GroupLeaderHandler groupLeaderHandler = new GroupLeaderHandler();
DepartmentLeaderHandler departmentLeaderHandler = new DepartmentLeaderHandler();
FinancialHandler financialHandler = new FinancialHandler();
groupLeaderHandler.setNext(departmentLeaderHandler);
departmentLeaderHandler.setNext(financialHandler);
//2.构建证明对象
IncomeCertification myIncomeCert = new IncomeCertification();
myIncomeCert.setIncome(1000);
myIncomeCert.setName("income certification");
myIncomeCert.setOwner("ruiruiyuzhi");
myIncomeCert.setType(1);
这时候我们就需要进入责任链开始审核了,但是入口在哪呢?当然,你可以强行把GroupLeaderHandler中加入一个public方法,作为入口,但是这样做扩展性不好,比方说这条审核的链条有新环节加入,或者有环节精简,改变了入口,岂不是需要重新编写入口?
其实我们可以把入口放在Handler中,这个方法意味着从当前环节开始,往下走,这样我们只需要根据业务需要改变入口对象即可:
public abstract class Handler {
protected Handler next;
abstract protected boolean processCertification(Certification cert);
public Handler getNext() {
return next;
}
public void setNext(Handler next) {
this.next = next;
}
//从当前结点开始,往下走
public boolean processFromCurrentHandler(Certification cert) {
return this.processCertification(cert);
}
}
这样,我们在Main方法中去测试整个流程可以发现,责任链的形式跑通了。
public class MainApplication {
public static void main(String[] args) {
GroupLeaderHandler groupLeaderHandler = new GroupLeaderHandler();
DepartmentLeaderHandler departmentLeaderHandler = new DepartmentLeaderHandler();
FinancialHandler financialHandler = new FinancialHandler();
groupLeaderHandler.setNext(departmentLeaderHandler);
departmentLeaderHandler.setNext(financialHandler);
IncomeCertification myIncomeCert = new IncomeCertification();
myIncomeCert.setIncome(1000);
myIncomeCert.setName("income certification");
myIncomeCert.setOwner("ruiruiyuzhi");
myIncomeCert.setType(1);
boolean isPass = groupLeaderHandler.processFromCurrentHandler(myIncomeCert);
if (isPass) {
System.out.println("get the certification");
} else {
System.out.println("reject");
}
}
}
image.png