責任鏈模式
將能夠處理同一類請求的對象連成一條鏈,所提交的請求沿着鏈傳遞,鏈上的對象逐個判斷是否有能力處理該請求,如果能則處理,如果不能則傳遞給鏈上的下一個對象。
場景
打牌時,輪流出牌;接力賽跑;大學中,獎學金審批;公司中,公文審批。
常見的公司內的請假審批流程:如果請假天數小於3天,主任審批;如果請假天數大於等於3天,小於10天,經理審批;如果大於等於10天,小於30天,總經理審批;如果大於等於30天,則自動拒絕。
責任鏈模式demo
實現這樣一個審批,SCM(Supply Chain Management 供應鏈管理)系統中,採購審批子系統的設計:
採購金額小於5萬,主任審批;
採購金額大於等於5萬,小於20萬,經理審批;
採購金額大於等於20萬,總經理審批。
新建PurchaseRequest類封裝採購申請的基本信息(申請人姓名,申請金額,用途);新建一個抽象類Leader,然後主任類Director,經理類Manager,總經理類GeneralManager,代碼如下所示。
public class PurchaseRequest {
//申請人姓名
private String empName;
//申請金額
private BigDecimal amount;
//申請原因(用途)
private String reason;
public PurchaseRequest(String empName, BigDecimal amount, String reason) {
this.empName = empName;
this.amount = amount;
this.reason = reason;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}
public abstract class Leader {
protected String name;
//責任鏈上的後繼對象
protected Leader nextLeader;
public Leader(String name) {
this.name = name;
}
//設定責任鏈上的後繼對象
public void setNextLeader(Leader nextLeader) {
this.nextLeader = nextLeader;
}
//處理請求的核心的業務方法
public abstract void handleRequest(PurchaseRequest request);
}
public class Director extends Leader{
public Director(String name) {
super(name);
}
@Override
public void handleRequest(PurchaseRequest request) {
if (request.getAmount().compareTo(new BigDecimal(50000)) == -1){
System.out.println("員工:" + request.getEmpName() +
"申請,採購金額" + request.getAmount() +
",原因(用途):" + request.getReason());
System.out.println("主任:" + this.name + ",審批通過!");
} else {
if (nextLeader != null){
this.nextLeader.handleRequest(request);
}
}
}
}
public class Manager extends Leader{
public Manager(String name) {
super(name);
}
@Override
public void handleRequest(PurchaseRequest request) {
if (request.getAmount().compareTo(new BigDecimal(50000)) > -1 &&
request.getAmount().compareTo(new BigDecimal(200000)) == -1){
System.out.println("員工:" + request.getEmpName() +
"申請,採購金額" + request.getAmount() +
",原因(用途):" + request.getReason());
System.out.println("經理:" + this.name + ",審批通過!");
} else {
if (nextLeader != null){
this.nextLeader.handleRequest(request);
}
}
}
}
public class GeneralManager extends Leader{
public GeneralManager(String name) {
super(name);
}
@Override
public void handleRequest(PurchaseRequest request) {
if (request.getAmount().compareTo(new BigDecimal(200000)) < 1){
System.out.println("員工:" + request.getEmpName() +
"申請,採購金額" + request.getAmount() +
",原因(用途):" + request.getReason());
System.out.println("總經理:" + this.name + ",審批通過!");
} else {
System.out.println("採購金額太大,請分批申請!");
}
}
}
public class Client {
public static void main(String[] args) {
Leader a = new Director("張主任");
Leader b = new Manager("李經理");
Leader c = new GeneralManager("趙總");
//組織責任鏈對象的關係
a.setNextLeader(b);
b.setNextLeader(c);
//開始採購金額審批
PurchaseRequest request = new PurchaseRequest("小明", new BigDecimal(6000),"採購年會禮品!");
a.handleRequest(request);
}
}
當申請金額爲6000時,結果如圖所示:
如果將金額修改爲15萬,則運行結果爲:
如果公司設置了一個副總經理的職位,然後修改審批流程爲:
採購金額小於5萬,主任審批;
採購金額大於等於5萬,小於10萬,經理審批;
採購金額大於等於10萬,小於20萬,副總經理審批;
採購金額大於等於20萬,總經理審批。
只需要新增一個副總經理的類,然後將經理類Manager的審批金額上限修改爲10萬:
public class ViceGeneralManager extends Leader{
public ViceGeneralManager(String name) {
super(name);
}
@Override
public void handleRequest(PurchaseRequest request) {
if (request.getAmount().compareTo(new BigDecimal(100000)) > -1 &&
request.getAmount().compareTo(new BigDecimal(200000)) == -1){
System.out.println("員工:" + request.getEmpName() +
"申請,採購金額" + request.getAmount() +
",原因(用途):" + request.getReason());
System.out.println("副總經理:" + this.name + ",審批通過!");
} else {
if (nextLeader != null){
this.nextLeader.handleRequest(request);
}
}
}
}
public class Client {
public static void main(String[] args) {
Leader a = new Director("張主任");
Leader b = new Manager("李經理");
Leader c = new ViceGeneralManager("孫副總");
Leader d = new GeneralManager("趙總");
//組織責任鏈對象的關係
a.setNextLeader(b);
b.setNextLeader(c);
c.setNextLeader(d);
//開始採購金額審批
PurchaseRequest request = new PurchaseRequest("小明", new BigDecimal(150000),"採購年會禮品!");
a.handleRequest(request);
}
}
則運行結果爲:
可以看到,當申請金額爲15萬時,原來由經理審批,現在由副總經理審批,符合需求。
由於責任鏈的創建完全在客戶端,因此新增新的具體處理類對原有類庫沒有任何影響,只需要添加新類,然後在客戶端調用時添加即可。符合開閉原則。
類圖
idea自動生成的類圖如下所示:
非鏈表方式實現責任鏈
以上爲鏈表方式定義職責鏈的demo。還可以用非鏈表方式實現職責鏈。
通過集合、數組生成責任鏈模式更加實用。實際上,很多項目中,每個具體的handler並不是由開發團隊定義的,而是項目上線後由外部單位追加的,所以使用鏈表方式定義COR鏈就很困難。
開發中常見的場景
Java中,異常機制就是一種責任鏈模式。一個try可以對應多個catch,當第一個catch不匹配類型,則自動跳到第二個catch。
Javascript語言中,事件的冒泡和捕獲機制。Java語言中,事件的處理採用觀察者模式。
Servlet開發中,過濾器的鏈式處理。
Struts2中,攔截器的調用也是典型的責任鏈模式。
以上爲責任鏈模式的學習筆記,此文章爲尚學堂視頻的學習筆記+自己總結。