責(zé)任鏈設(shè)計(jì)模式
簡單介紹
View的事件分發(fā)機(jī)制是責(zé)任鏈(Chain of Responsibility)設(shè)計(jì)模式的典型應(yīng)用,其它經(jīng)典的應(yīng)用場景還有:JavaWeb 的過濾器、攔截器,Servlet中的請求響應(yīng)鏈;okhttp開源庫中也是在網(wǎng)絡(luò)層、應(yīng)用層中使用攔截器來進(jìn)行分層解耦,使的網(wǎng)絡(luò)層的配置和開發(fā)變得簡單而優(yōu)雅。
案例分析
設(shè)計(jì)模式并不是一個(gè)抽象的概念,而是在編程的實(shí)踐中總結(jié)出的用于解決某類典型問題的典型范式。可以說,設(shè)計(jì)模式是為實(shí)戰(zhàn)而生的。這里說的責(zé)任鏈設(shè)計(jì)模式也不例外,下面看一個(gè)真實(shí)的應(yīng)用場景:
員工請假流程,普通員工三天以內(nèi)的請假申請,TeamLeader直接可以直接審批通過;三到五天的請假申請由TeamLeader審批通過后技術(shù)總監(jiān)(CTO)可以審批通過;五到十天的請假申請就必須得到技術(shù)總監(jiān)(CTO)的審批通過后部門經(jīng)理(PV)可以審批通過;而十天以上的請假申請就必須得到部門經(jīng)理(PV)和總經(jīng)理(CEO)的審批通過后才能正常休假。
員工休假申請,只需要找到自己的直接上級申請就可以了;直接上級可以拒絕員工的請假,如果請假時(shí)長超出直接上級的審批范圍,直接上級可以向上提交該員工的請假申請。經(jīng)過具有批準(zhǔn)權(quán)限的兩級的批準(zhǔn),員工才可以正常休假。
實(shí)例流程,如下所示:
程序員提交了了角色為【程序員-小林】提交的休假請求;時(shí)長:【2】天。
項(xiàng)目組長攔截了角色為【程序員-小林】提交的休假請求;時(shí)長:【2】天。
項(xiàng)目組長已批準(zhǔn)角色為【程序員-小林】提交的休假請求;時(shí)長:【2】天。
程序員提交了了角色為【程序員-大鵬】提交的休假請求;時(shí)長:【3】天。
項(xiàng)目組長提交了了角色為【程序員-大鵬】提交的休假請求;時(shí)長:【3】天。
技術(shù)經(jīng)理攔截了角色為【程序員-大鵬】提交的休假請求;時(shí)長:【3】天。
技術(shù)經(jīng)理已批準(zhǔn)角色為【程序員-大鵬】提交的休假請求;時(shí)長:【3】天。
程序員提交了了角色為【程序員-樊勝美】提交的休假請求;時(shí)長:【5】天。
項(xiàng)目組長提交了了角色為【程序員-樊勝美】提交的休假請求;時(shí)長:【5】天。
技術(shù)經(jīng)理提交了了角色為【程序員-樊勝美】提交的休假請求;時(shí)長:【5】天。
部門經(jīng)理攔截了角色為【程序員-樊勝美】提交的休假請求;時(shí)長:【5】天。
部門經(jīng)理已批準(zhǔn)角色為【程序員-樊勝美】提交的休假請求;時(shí)長:【5】天。
程序員提交了了角色為【程序員-小蚯蚓】提交的休假請求;時(shí)長:【10】天。
項(xiàng)目組長提交了了角色為【程序員-小蚯蚓】提交的休假請求;時(shí)長:【10】天。
技術(shù)經(jīng)理提交了了角色為【程序員-小蚯蚓】提交的休假請求;時(shí)長:【10】天。
部門經(jīng)理提交了了角色為【程序員-小蚯蚓】提交的休假請求;時(shí)長:【10】天。
首席執(zhí)行官攔截了角色為【程序員-小蚯蚓】提交的休假請求;時(shí)長:【10】天。
首席執(zhí)行官已批準(zhǔn)角色為【程序員-小蚯蚓】提交的休假請求;時(shí)長:【10】天。
項(xiàng)目組長提交了了角色為【項(xiàng)目組長-關(guān)關(guān)】提交的休假請求;時(shí)長:【12】天。
技術(shù)經(jīng)理提交了了角色為【項(xiàng)目組長-關(guān)關(guān)】提交的休假請求;時(shí)長:【12】天。
部門經(jīng)理提交了了角色為【項(xiàng)目組長-關(guān)關(guān)】提交的休假請求;時(shí)長:【12】天。
首席執(zhí)行官攔截了角色為【項(xiàng)目組長-關(guān)關(guān)】提交的休假請求;時(shí)長:【12】天。
首席執(zhí)行官已批準(zhǔn)角色為【項(xiàng)目組長-關(guān)關(guān)】提交的休假請求;時(shí)長:【12】天。
項(xiàng)目組長提交了了角色為【項(xiàng)目組長-應(yīng)勤】提交的休假請求;時(shí)長:【14】天。
技術(shù)經(jīng)理提交了了角色為【項(xiàng)目組長-應(yīng)勤】提交的休假請求;時(shí)長:【14】天。
部門經(jīng)理提交了了角色為【項(xiàng)目組長-應(yīng)勤】提交的休假請求;時(shí)長:【14】天。
首席執(zhí)行官攔截了角色為【項(xiàng)目組長-應(yīng)勤】提交的休假請求;時(shí)長:【14】天。
首席執(zhí)行官已拒絕角色為【項(xiàng)目組長-應(yīng)勤】提交的休假請求;時(shí)長:【14】天。
技術(shù)經(jīng)理提交了了角色為【技術(shù)經(jīng)理-張璇】提交的休假請求;時(shí)長:【20】天。
部門經(jīng)理提交了了角色為【技術(shù)經(jīng)理-張璇】提交的休假請求;時(shí)長:【20】天。
首席執(zhí)行官攔截了角色為【技術(shù)經(jīng)理-張璇】提交的休假請求;時(shí)長:【20】天。
首席執(zhí)行官已批準(zhǔn)角色為【技術(shù)經(jīng)理-張璇】提交的休假請求;時(shí)長:【20】天。
部門經(jīng)理提交了了角色為【部門經(jīng)理-包奕凡】提交的休假請求;時(shí)長:【60】天。
首席執(zhí)行官攔截了角色為【部門經(jīng)理-包奕凡】提交的休假請求;時(shí)長:【60】天。
首席執(zhí)行官已批準(zhǔn)角色為【部門經(jīng)理-包奕凡】提交的休假請求;時(shí)長:【60】天。
首席執(zhí)行官提交了了角色為【首席執(zhí)行官-曲筱綃】提交的休假請求;時(shí)長:【120】天。
Master攔截了角色為【首席執(zhí)行官-曲筱綃】提交的休假請求;時(shí)長:【120】天。
Master拒絕了角色為【首席執(zhí)行官-曲筱綃】提交的休假請求;時(shí)長:【120】天。
代碼設(shè)計(jì)
既然知曉了使用責(zé)任鏈解決問題,那么按照責(zé)任鏈的思想解決現(xiàn)實(shí)問題。抽象休假處理的接口,Java中接口定義規(guī)則。如下圖,IHandler接口聲明了三個(gè)方法,dispatch開頭的方法是提交請假申請的方法;onIntercept開頭的方法表示是否攔截處理這個(gè)休假申請;handle開頭的方法表示處理這個(gè)請求,可以批準(zhǔn)和拒絕該申請,自此休假申請不在向上級提交。
package me.ziuo.design_pattern.cop.employee;
/**
*
* @author ziyuo
* @Description 抽象出來的休假申請?zhí)幚斫涌? */
public interface IHandler {
/**
* 分發(fā)請假請求
* @param askModel
* @return 分發(fā)結(jié)果
*/
boolean dispatchAsk(LeaveAskModel askModel);
/**
* 攔截請假請求
* @param askModel
* @return 攔截與否
*/
boolean onInterceptAsk(LeaveAskModel askModel);
/**
* 處理請假請求
* @param askModel
* @return 是否批準(zhǔn)
*/
boolean handleAsk(LeaveAskModel askModel);
}
LeaveAskModel 休假請求類,該數(shù)據(jù)模型里面保存了修改的天數(shù)和申請人信息。如圖所示。
package me.ziuo.design_pattern.cop.employee;
/**
*
* @author ziyuo
* @Description 代表請假申請信息對象
*/
public class LeaveAskModel {
private int days;// 休假天數(shù)
private Employee askEmp;// 申請人
public LeaveAskModel(int days, Employee askEmp) {
super();
this.days = days;
this.askEmp = askEmp;
}
public int getDays() {
return days;
}
public void setDays(int days) {
this.days = days;
}
public Employee getAskEmp() {
return askEmp;
}
public void setAskEmp(Employee askEmp) {
this.askEmp = askEmp;
}
}
員工的抽象類實(shí)現(xiàn)IHandler里面的定義的接口的相關(guān)的方法,里面包含了員工角色信息、員工名稱信息、可處理的最大申請?zhí)鞌?shù)、直屬領(lǐng)導(dǎo)(當(dāng)無權(quán)限處理休假申請的時(shí)候可以向上提交)。
其中實(shí)現(xiàn)IHandler的方法介紹
dispatch開頭的方法的具體含義是首先判斷本角色是否攔截這個(gè)請求,如果攔截則調(diào)用請求的方法,是否審批通過取決于handleAsk的結(jié)果。
onIntercept開頭方法的具體含義,是當(dāng)角色類型是否不同于提交的對象時(shí),如果提交者申請的提交時(shí)長可以處理的情況下,選擇攔截該請求后請求響應(yīng)的審批處理。
handle開頭的方法用于進(jìn)行審批請求,邏輯大概是這樣:根據(jù)員工的信息來進(jìn)行響應(yīng)的處理,這里只是一個(gè)實(shí)現(xiàn)案例。
如圖所示。
package me.ziuo.design_pattern.cop.employee;
public abstract class Employee implements IHandler{
private String actor;//員工角色名稱
private String name;//員工名稱
private int maxHandleDay;//最大可審批的請假天數(shù)(不包含最大天數(shù))
private Employee leader;//直屬領(lǐng)導(dǎo)
public Employee(String actor, int maxHandleDay) {
super();
this.actor = actor;
this.maxHandleDay = maxHandleDay;
}
public String getActor() {
return actor;
}
public void setActor(String actor) {
this.actor = actor;
}
public int getMaxHandleDay() {
return maxHandleDay;
}
public void setMaxHandleDay(int maxHandleDay) {
this.maxHandleDay = maxHandleDay;
}
public boolean dispatchAsk(LeaveAskModel askModel) {
if(onInterceptAsk(askModel)){
return handleAsk(askModel);
}else{
if(getLeader()!=null){
System.out.println(getActor()+"提交了了角色為【"+askModel.getAskEmp().actor+"】提交的休假請求;時(shí)長:【"+askModel.getDays()+"】天。");
return getLeader().dispatchAsk(askModel);
}else{
System.out.println(getActor()+"提交了了角色為【"+askModel.getAskEmp().actor+"】提交的休假請求;時(shí)長:【"+askModel.getDays()+"】天。");
//寫這段代碼的程序員進(jìn)行審批 ,終于站在了食物鏈的頂端(原來是在做夢)。
System.out.println("【代碼作者】"+"攔截了角色為【"+askModel.getAskEmp().actor+"】提交的休假請求;時(shí)長:【"+askModel.getDays()+"】天。");
System.out.println("【代碼作者】"+"拒絕了角色為【"+askModel.getAskEmp().actor+"】提交的休假請求;時(shí)長:【"+askModel.getDays()+"】天。");
return false;
}
}
}
public boolean onInterceptAsk(LeaveAskModel askModel) {
if(askModel!=null&&!this.getClass().equals(askModel.getAskEmp().getClass())&&askModel.getDays()<this.getMaxHandleDay()){
System.out.println(getActor()+"攔截了角色為【"+askModel.getAskEmp().actor+"】提交的休假請求;時(shí)長:【"+askModel.getDays()+"】天。");
return true;
}
return false;
}
public boolean handleAsk(LeaveAskModel askModel) {
if(askModel==null||askModel.getAskEmp().getName().equals("小帥哥0")){
System.out.println(getActor()+"已拒絕角色為【"+askModel.getAskEmp().actor+"】提交的休假請求;時(shí)長:【"+askModel.getDays()+"】天。");
return false;
}
if(askModel!=null&&askModel.getDays()<this.getMaxHandleDay()){
System.out.println(getActor()+"已批準(zhǔn)角色為【"+askModel.getAskEmp().actor+"】提交的休假請求;時(shí)長:【"+askModel.getDays()+"】天。");
return true;
}
return false;
}
public Employee getLeader() {
return leader;
}
public void setLeader(Employee leader) {
this.leader = leader;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
各個(gè)對象的封裝案例,下面列出來。
程序員類代碼
package me.ziuo.design_pattern.cop.employee;
public class Programmer extends Employee {
public Programmer() {
this("程序員", 0);
}
public Programmer(String actor, int maxHandleDay) {
super("程序員", 0);
setLeader(new TeamLeader());
}
public boolean dispatchAsk(LeaveAskModel askModel) {
return super.dispatchAsk(askModel);
}
public boolean onInterceptAsk(LeaveAskModel askModel) {
return !(askModel.getAskEmp() instanceof Programmer);
}
}
TeamLeader類代碼
package me.ziuo.design_pattern.cop.employee;
public class TeamLeader extends Employee {
public TeamLeader() {
this("項(xiàng)目組長", 3);
}
public TeamLeader(String actor, int maxHandleDay) {
super("項(xiàng)目組長", 3);
setLeader(new CTO());
}
}
CTO類代碼
package me.ziuo.design_pattern.cop.employee;
public class CTO extends Employee {
public CTO() {
this("技術(shù)經(jīng)理", 5);
}
public CTO(String actor, int maxHandleDay) {
super("技術(shù)經(jīng)理", 5);
setLeader(new PV());
}
}
PV類代碼
package me.ziuo.design_pattern.cop.employee;
public class PV extends Employee {
public PV(){
this("部門經(jīng)理", 10);
}
public PV(String actor, int maxHandleDay) {
super("部門經(jīng)理", 10);
setLeader(new CEO());
}
}
CEO類代碼
package me.ziuo.design_pattern.cop.employee;
public class CEO extends Employee {
public CEO(){
this("首席執(zhí)行官", 120);
}
public CEO(String actor, int maxHandleDay) {
super("首席執(zhí)行官", 120);
setLeader(null);
}
}
各個(gè)角色的向上傳遞休假申請的處理鏈,就組成了所謂的責(zé)任鏈;除了頂層的CEO其余的角色都持有他的直接領(lǐng)導(dǎo)。這條持有直接領(lǐng)導(dǎo)的鏈條就組成了一個(gè)單向鏈表,而責(zé)任鏈模式的部分場景中會直接使用單向的鏈表來鏈接責(zé)任處理集合。
責(zé)任鏈設(shè)計(jì)模到此就介紹完畢了。
如果希望了解Android事件分發(fā)流程對責(zé)任鏈設(shè)計(jì)模式的運(yùn)用請移步下面的鏈接。
Android事件分發(fā)流程(二)源碼解析