一、簡介
職責鏈設計模式是屬於經典設計模式中行爲型設計模式裏的一種設計模式。其實這種模式
在現實生活中很多地方出現,比如說:
1.多人打牌:
上家出牌後,出牌請求到達下家,下家出牌後,下下家獲得出牌機會, 在一輪後如果無人出牌,則可以從
新下一輪出牌,這樣一個場景,其實就是職責鏈模式的原型。
2.審批流程:
再比如,一個公司的採購單審批流程,如果採購單總價在5萬RMB,那麼主任審覈即可, 如果5-10萬RMB
由副董事長審批,10-50萬由董事長審批,>=50萬則由董事會審批。每一個節點只負責職責內的訂單額度,
如果自己沒有權限,則給自己的下節點進行轉發申請。
二、模式實現
上面的兩個場景我們調選一個進行實現,我們就用進階的方式從最簡單的設計一步一步往目標設計走。這裏我選擇2,審批流程的實現進行設計。初始代碼如下:
原始設計
class AuditHandler {
// @TODO 遞交採購單給主任
public void sendRequestToDirector(PurchaseRequest request) {
if (request.getAmount() < 50000) {
// @TODO 主任可審批該採購單
this.handleByDirector(request);
} else if (request.getAmount() < 100000) {
// @TODO 副董事長可審批該採購單
this.handleByVicePresident(request);
} else if (request.getAmount() < 500000) {
// @TODO 董事長可審批該採購單
this.handleByPresident(request);
} else {
// @TODO 董事會可審批該採購單
this.handleByCongress(request);
}
}
// @TODO 主任審批採購單
public void handleByDirector(PurchaseRequest request) {
//代碼省略
}
// @TODO 副董事長審批採購單
public void handleByVicePresident(PurchaseRequest request) {
//代碼省略
}
// @TODO 董事長審批採購單
public void handleByPresident(PurchaseRequest request) {
//代碼省略
}
// @TODO 董事會審批採購單
public void handleByCongress(PurchaseRequest request) {
//代碼省略
}
}
class PurchaseRequest {
private long amount;
public long getAmount() {
return amount;
}
}
上面這種設計方式可以處理完成相應的業務,但違反了:單一職責原則,開閉原則。一個類集中了過多的職責,如果一旦需要在期間進行擴展一個角色進行審批,就需要改動原有的代碼。並且這種設計模式是無法實現使用方可定製化的,因爲都寫死了。
升級設計
爲了解決上面設計的原則缺陷,我們進行進行設計升級,把多個職責進行拆分,獨立出單個處理類。使用一個抽象的處理類,實現各個鏈節點的關聯與處理細節
/**
* @author chandlerHuang
* @description @TODO 抽象處理類
* @date 2020/3/27
*/
public abstract class AbstractHandler {
// @TODO 處理連接
protected AbstractHandler abstractHandler;
public AbstractHandler(AbstractHandler abstractHandler) {
this.abstractHandler = abstractHandler;
}
public void setAbstractHandler(AbstractHandler abstractHandler) {
this.abstractHandler = abstractHandler;
}
public abstract void handle(PurchaseRequest request);
}
class Handler extends AbstractHandler{
// 所指崗位
private String position;
// 處理金額上限
private double amount;
public void setPosition(String position) {
this.position = position;
}
public void setAmount(long amount) {
this.amount = amount;
}
public Handler(AbstractHandler abstractHandler,String position, long amount) {
super(abstractHandler);
this.amount = amount;
this.position = position;
}
@Override
public void handle(PurchaseRequest request) {
if(this.amount>request.getAmount()){
// @TODO 處理請求
System.out.println(this.position+"審覈處理採購單:"+request.getNumber() +"->"+ request.getPurpose()+" 購買金額:"+request.getAmount());
}else {
// @TODO 交給下一節點處理
this.abstractHandler.handle(request);
}
}
}
class PurchaseRequest {
//採購金額
private double amount;
//採購單編號
private int number;
//採購目的、備註
private String purpose;
public PurchaseRequest(double amount, int number, String purpose) {
this.amount = amount;
this.number = number;
this.purpose = purpose;
}
public double getAmount() {
return amount;
}
public int getNumber() {
return number;
}
public String getPurpose() {
return purpose;
}
public void setAmount(double amount) {
this.amount = amount;
}
public void setNumber(int number) {
this.number = number;
}
public void setPurpose(String purpose) {
this.purpose = purpose;
}
}
這裏面對於責任鏈的構建是交給了使用方,這樣極大的增強了這個設計的代碼複用率,可擴展性以及流程的可變性。
這裏我們來演示一下使用方式,構建流程處理鏈。
客戶端調用
/**
* @author chandlerHuang
* @description @TODO
* @date 2020/3/27
*/
public class Test {
public static void main(String[] args) {
// @TODO 構建流程鏈
Handler handler = init();
// @TODO 構建採購單
List<PurchaseRequest> requests = buildRequest();
// @TODO 處理
requests.forEach(request->{
handler.handle(request);
});
}
private static List<PurchaseRequest> buildRequest() {
List<PurchaseRequest> datas = new ArrayList<>(4);
PurchaseRequest data1 = new PurchaseRequest(39000.00,10001,"購買雲服務器");
PurchaseRequest data2 = new PurchaseRequest(89000.00,10002,"採購辦公電腦與辦公用品");
PurchaseRequest data3 = new PurchaseRequest(490430.87,10003,"分公司裝修裝潢採購");
PurchaseRequest data4 = new PurchaseRequest(1189490430,10004,"購置地皮建廠");
datas.add(data1);
datas.add(data2);
datas.add(data3);
datas.add(data4);
return datas;
}
private static Handler init() {
// 董事會節點
Handler handler4 = new Handler(null,"董事會",Double.MAX_VALUE);
// 董事長節點
Handler handler3 = new Handler(handler4,"董事長",500000.00);
// 副董事長節點
Handler handler2 = new Handler(handler3,"副董事長", 100000.00);
// 財務主任節點
Handler handler = new Handler(handler2,"財務主任", 50000.00);
return handler;
}
}
上面的代碼顯示了,如果你需要調整節點,只需要在構建流程鏈內處理(客戶端|使用方),而我們的設計方不需要進行改動,將發送方與處理方進行了解耦。無需關心具體處理節點。
職責鏈模式優點
1.職責鏈模式使得一個對象無須知道是其他哪一個對象處理其請求,對象僅需知道該請求會 被處理即可,接收者和發送者都沒有對方的明確信息,且鏈中的對象不需要知道鏈的結構, 由客戶端負責鏈的創建,降低了系統的耦合度。
2.請求處理對象僅需維持一個指向其後繼者的引用,而不需要維持它對所有的候選處理者的 引用,可簡化對象的相互連接。
職責鏈模式缺點
1.由於一個請求沒有明確的接收者,那麼就不能保證它一定會被處理,該請求可能一直到鏈 的末端都得不到處理;一個請求也可能因職責鏈沒有被正確配置而得不到處理。
2.對於比較長的職責鏈,請求的處理可能涉及到多個處理對象,系統性能將受到一定影響, 而且在進行代碼調試時不太方便。
3.如果建鏈不當,可能會造成循環調用,將導致系統陷入死循環。
合適使用場景
1.有多個對象可以處理同一個請求,具體哪個對象處理該請求待運行時刻再確定,客戶端只 需將請求提交到鏈上,而無須關心請求的處理對象是誰以及它是如何處理的。
2.在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。
3.可動態指定一組對象處理請求,客戶端可以動態創建職責鏈來處理請求,還可以改變鏈中 處理者之間的先後次序。
三、思考題
上面設計處理責任鏈模式處理採購單審批。請各位讀者採用職責鏈模式設計:標準鬥地主程序.