責(zé)任鏈模式(Chain of Responsibility Pattern)為請求創(chuàng)建了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發(fā)送者和接收者進行解耦。這種類型的設(shè)計模式屬于行為型模式。
在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個對象不能處理該請求,那么它會把相同的請求傳給下一個接收者,依此類推。
在現(xiàn)實生活中,常常會出現(xiàn)這樣的事例:一個請求有多個對象可以處理,但每個對象的處理條件或權(quán)限不同。例如,公司員工請假,可批假的領(lǐng)導(dǎo)有部門負責(zé)人、副總經(jīng)理、總經(jīng)理等,但每個領(lǐng)導(dǎo)能批準(zhǔn)的天數(shù)不同,員工必須根據(jù)自己要請假的天數(shù)去找不同的領(lǐng)導(dǎo)簽名,也就是說員工必須記住每個領(lǐng)導(dǎo)的姓名、電話和地址等信息,這增加了難度。這樣的例子還有很多,如找領(lǐng)導(dǎo)出差報銷、生活中的“擊鼓傳花”游戲等。
在計算機軟硬件中也有相關(guān)例子,如總線網(wǎng)中數(shù)據(jù)報傳送,每臺計算機根據(jù)目標(biāo)地址是否同自己的地址相同來決定是否接收;還有異常處理中,處理程序根據(jù)異常的類型決定自己是否處理該異常;還有 Struts2 的攔截器、JSP 和 Servlet 的 Filter 等,所有這些,如果用責(zé)任鏈模式都能很好解決。
職責(zé)鏈模式主要包含以下角色:
現(xiàn)需要開發(fā)一個請假流程控制系統(tǒng)。請假一天以下的假只需要小組長同意即可;請假1天到3天的假還需要部門經(jīng)理同意;請求3天到7天還需要總經(jīng)理同意才行。
請假條
public class LeaveRequest {private String name;//姓名
private int num;//請假天數(shù)
private String content;//請假內(nèi)容
public LeaveRequest(String name, int num, String content) {this.name = name;
this.num = num;
this.content = content;
}
public String getName() {return name;
}
public int getNum() {return num;
}
public String getContent() {return content;
}
}
處理者抽象類
public abstract class Handler {protected final static int NUM_ONE = 1;
protected final static int NUM_THREE = 3;
protected final static int NUM_SEVEN = 7;
//該領(lǐng)導(dǎo)處理的請假天數(shù)區(qū)間
private int numStart;
private int numEnd;
//領(lǐng)導(dǎo)上面還有領(lǐng)導(dǎo)
private Handler nextHandler;
//設(shè)置請假天數(shù)范圍 上不封頂
public Handler(int numStart) {this.numStart = numStart;
}
//設(shè)置請假天數(shù)范圍
public Handler(int numStart, int numEnd) {this.numStart = numStart;
this.numEnd = numEnd;
}
//設(shè)置上級領(lǐng)導(dǎo)
public void setNextHandler(Handler nextHandler){this.nextHandler = nextHandler;
}
//提交請假條
public final void submit(LeaveRequest leave){if(0 == this.numStart){return;
}
//如果請假天數(shù)達到該領(lǐng)導(dǎo)者的處理要求
if(leave.getNum() >= this.numStart){this.handleLeave(leave);
//如果還有上級 并且請假天數(shù)超過了當(dāng)前領(lǐng)導(dǎo)的處理范圍
if(null != this.nextHandler && leave.getNum() >numEnd){this.nextHandler.submit(leave);//繼續(xù)提交
} else {System.out.println("流程結(jié)束");
}
}
}
//各級領(lǐng)導(dǎo)處理請假條方法
protected abstract void handleLeave(LeaveRequest leave);
}
處理者實現(xiàn)類
//小組長
public class GroupLeader extends Handler {public GroupLeader() {//小組長處理1-3天的請假
super(Handler.NUM_ONE, Handler.NUM_THREE);
}
@Override
protected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "請假" + leave.getNum() + "天," + leave.getContent() + "。");
System.out.println("小組長審批:同意。");
}
}
//部門經(jīng)理
public class Manager extends Handler {public Manager() {//部門經(jīng)理處理3-7天的請假
super(Handler.NUM_THREE, Handler.NUM_SEVEN);
}
@Override
protected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "請假" + leave.getNum() + "天," + leave.getContent() + "。");
System.out.println("部門經(jīng)理審批:同意。");
}
}
//總經(jīng)理
public class GeneralManager extends Handler {public GeneralManager() {//部門經(jīng)理處理7天以上的請假
super(Handler.NUM_SEVEN);
}
@Override
protected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "請假" + leave.getNum() + "天," + leave.getContent() + "。");
System.out.println("總經(jīng)理審批:同意。");
}
}
客戶端
public class Client {public static void main(String[] args) {//請假條來一張
LeaveRequest leave = new LeaveRequest("小黃",8,"身體不適");
//各位領(lǐng)導(dǎo)
GroupLeader groupLeader = new GroupLeader();
Manager manager = new Manager();
GeneralManager generalManager = new GeneralManager();
groupLeader.setNextHandler(manager);//小組長的領(lǐng)導(dǎo)是部門經(jīng)理
manager.setNextHandler(generalManager);//部門經(jīng)理的領(lǐng)導(dǎo)是總經(jīng)理
//之所以在這里設(shè)置上級領(lǐng)導(dǎo),是因為可以根據(jù)實際需求來更改設(shè)置,如果實戰(zhàn)中上級領(lǐng)導(dǎo)人都是固定的,則可以移到領(lǐng)導(dǎo)實現(xiàn)類中。
//提交申請
groupLeader.submit(leave);
}
}
優(yōu)點
降低耦合度,使請求的發(fā)送者和接收者解耦,便于靈活的、可插拔的定義請求處理過程;
簡化、封裝了請求的處理過程,并且這個過程對客戶端而言是透明的,以便于動態(tài)地重新組織鏈以及分配責(zé)任,增強請求處理的靈活性;
可以從職責(zé)鏈任何一個節(jié)點開始,也可以隨時改變內(nèi)部的請求處理規(guī)則,每個請求處理者都可以去動態(tài)地指定他的繼任者;
職責(zé)鏈可簡化對象間的相互連接。它們僅需保持一個指向其后繼者的引用,而不需保持它所有的候選接受者的引用;
增加新的請求處理類很方便。
缺點
在JavaWeb應(yīng)用開發(fā)中,F(xiàn)ilterChain是職責(zé)鏈(過濾器)模式的典型應(yīng)用,以下是Filter的模擬實現(xiàn)分析:
模擬web請求Request以及web響應(yīng)Response
public interface Request{}
public interface Response{}
模擬web過濾器Filter
public interface Filter {public void doFilter(Request req,Response res,FilterChain c);
}
模擬實現(xiàn)具體過濾器
public class FirstFilter implements Filter {@Override
public void doFilter(Request request, Response response, FilterChain chain) {System.out.println("過濾器1 前置處理");
// 先執(zhí)行所有request再倒序執(zhí)行所有response
chain.doFilter(request, response);
System.out.println("過濾器1 后置處理");
}
}
public class SecondFilter implements Filter {@Override
public void doFilter(Request request, Response response, FilterChain chain) {System.out.println("過濾器2 前置處理");
// 先執(zhí)行所有request再倒序執(zhí)行所有response
chain.doFilter(request, response);
System.out.println("過濾器2 后置處理");
}
}
模擬實現(xiàn)過濾器鏈FilterChain
public class FilterChain {private Listfilters = new ArrayList();
private int index = 0;
// 鏈?zhǔn)秸{(diào)用
public FilterChain addFilter(Filter filter) {this.filters.add(filter);
return this;
}
public void doFilter(Request request, Response response) {if (index == filters.size()) {return;
}
Filter filter = filters.get(index);
index++;
filter.doFilter(request, response, this);
}
}
測試類
public class Client {public static void main(String[] args) {Request req = null;
Response res = null ;
FilterChain filterChain = new FilterChain();
filterChain.addFilter(new FirstFilter()).addFilter(new SecondFilter());
filterChain.doFilter(req,res);
}
}
運行效果和我們javaweb中的效果一樣,要想理解為什么打印順序會這樣,需要重點去理解FilterChain
類中doFilter
方法:
職責(zé)鏈模式存在以下兩種情況。
純的責(zé)任鏈模式的實際例子很難找到,一般看到的例子均是不純的責(zé)任鏈模式的實現(xiàn)。
參考
Java 設(shè)計模式 —— 責(zé)任鏈模式
黑馬程序員
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧