職責鏈模式 案例引入 OA(Office Automation)系統的採購審批項目,需求是 1.採購員採購教學用品。 2.price >= 0 && price <= 5000 由教學主任審批 3.price > 5000 && price <= 10000 由原則審批 4.price > 1000 ...
職責鏈模式
案例引入
OA(Office Automation)系統的採購審批項目,需求是
1.採購員採購教學用品。
2.price >= 0 && price <= 5000 由教學主任審批
3.price > 5000 && price <= 10000 由原則審批
4.price > 10000 && price <= 30000 由副校長審批
5.price > 30000由校長審批
傳統方式完成採購審批任務
採購員類,採購請求類,以及審批組類(教學主任,院長等),每次採購人員,發起採購請求(創建一個採購請求對象),在這個對象中會依賴審批組類,然後採購請求對象,會根據此次的金額,if else分支自動匹配審批組中的類,進行審批。
傳統方式問題分析
- 1.傳統方式是,接收到一個採購請求後,根據採購金額來調用對應的Approver(審批人),完成審批。
- 2.會使用到分支判斷(如if else)來對不同的採購請求處理。這樣存在的問題,如果各個級別的人員審批金額發生變化,在客戶端也需要變化;客戶端必須明確的知道由多少個審批級別和訪問
- 3.這樣對一個採購請求進行處理,和Approver就存在強耦合關係,不利於代碼的擴展和維護。
- 4.解決方案 => 職責鏈模式。
基本介紹
- 1.職責鏈模式(Chain of Responsibility Pattern),也叫責任鏈模式,為請求創建了一個接收者對象的鏈。這種模式使請求的發送者和接收者進行解耦。
- 2.職責鏈模式通常每個接收者都包含對另一個接收者的引用。如果一個對象不能處理請求,那麼它會把相同的請求傳給下一個接收者,以此類推。
- 3.屬於行為型模式。
原理類圖
職責鏈模式使得多個對象,都有機會處理請求,從而避免請求的發送者和接收者間的耦合。將可以處理請求的對象,構建為一條鏈並沿著這條鏈傳遞該請求,只有一個處理者處理該請求。
原理類圖說明
- 1.Handler 抽象的處理者,定義了一個處理請求的方法,同時有一個Handler的屬性(用於子類定義下一個處理請求的對象)。
- 2.ConcreteHandlerA ,ConcreteHandlerB是具體的處理者,處理它自己複雜的請求,可以訪問它的後繼者(即下一個處理者),如果可以處理當前請求,則處理,否則就將該請求交個後繼者去處理,從而形成一個責任鏈。
- 3.Request 含有很多屬性,表示為一個要處理的請求。
職責鏈模式實現案例
原理類圖
代碼
/**
* @author 長名06
* @version 1.0
* 審批人抽象類
*/
public abstract class Approver {
Approver approver;
String name;
public Approver(String name){
this.name = name;
}
public void setApprover(Approver approver) {
this.approver = approver;
}
public abstract void processRequest(PurchaseRequest request);
}
/**
* @author 長名06
* @version 1.0
* 系主任審批人員
*/
public class DepartmenterApprover extends Approver{
public DepartmenterApprover(String name){
super(name);
}
@Override
public void processRequest(PurchaseRequest request) {
if(request.getPrice() >= 0 && request.getPrice() <= 5000){
System.out.println("請求編號" + request.getId() + "被" + this.name + "處理");
}else {
approver.processRequest(request);
}
}
}
/**
* @author 長名06
* @version 1.0
* 院長審批人員
*/
public class CollegeApprover extends Approver{
public CollegeApprover(String name){
super(name);
}
@Override
public void processRequest(PurchaseRequest request) {
if(request.getPrice() > 5000 && request.getPrice() <= 10000){
System.out.println("請求編號" + request.getId() + "被" + this.name + "處理");
}else {
approver.processRequest(request);
}
}
}
/**
* @author 長名06
* @version 1.0
* 副校長審批人員
*/
public class ViceSchoolMasterApprover extends Approver{
public ViceSchoolMasterApprover(String name){
super(name);
}
@Override
public void processRequest(PurchaseRequest request) {
if(request.getPrice() > 10000 && request.getPrice() <= 30000){
System.out.println("請求編號" + request.getId() + "被" + this.name + "處理");
}else {
approver.processRequest(request);
}
}
}
/**
* @author 長名06
* @version 1.0
* 校長審批人員
*/
public class SchoolMasterApprover extends Approver{
public SchoolMasterApprover(String name){
super(name);
}
@Override
public void processRequest(PurchaseRequest request) {
if(request.getPrice() > 30000){
System.out.println("請求編號" + request.getId() + "被" + this.name + "處理");
}else {
if(approver != null) {
approver.processRequest(request);
return;
}
throw new RuntimeException("處理鏈執行完畢,不能處理當前請求");
}
}
}
/**
* @author 長名06
* @version 1.0
*/
public class PurchaseRequest {
private int type = 0;
private float price = 0.0F;
private int id = 0;
public PurchaseRequest(int type, float price, int id) {
this.type = type;
this.price = price;
this.id = id;
}
public int getType() {
return type;
}
public float getPrice() {
return price;
}
public int getId() {
return id;
}
}
/**
* @author 長名06
* @version 1.0
*/
public class Client {
public static void main(String[] args) {
PurchaseRequest purchaseRequest = new PurchaseRequest(1, 4000, 1);
DepartmenterApprover departmenterApprover = new DepartmenterApprover("系主任");
CollegeApprover collegeApprover = new CollegeApprover("院長");
ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("副校長");
SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("校長");
//構建處理鏈 要形成一個環狀,保證請求一定要被處理,這樣的好處,就是如果當前是最後一個級別處理的
//要處理的請求,是在前幾個級別,依舊保證能被處理,不然如果使用最後一個級別處理請求,但是不符合此級別要求
//而最後一個級別未設置下一個處理人,則此請求無法被處理了
//一般情況下,都是從最低級別開始處理
departmenterApprover.setApprover(collegeApprover);
collegeApprover.setApprover(viceSchoolMasterApprover);
viceSchoolMasterApprover.setApprover(schoolMasterApprover);
schoolMasterApprover.setApprover(departmenterApprover);
departmenterApprover.processRequest(purchaseRequest);
}
}
職責鏈模式在SpringMVC框架的源碼分析
- 1.SpringMVC-HanlderExecutionChain類就使用到了職責鏈模式。
- 2.SpringMVC請求流程圖。
- 3.總結,流程圖中,執行了攔截器相關方法interceptor.preHandler()等,在springMVC處理請求時,使用到了職責鏈模式和適配器模式;HandlerExecutionChain主要負責的是請求攔截器的執行和請求處理,但是他自身不處理請求,只是將請求分配給鏈上註冊的處理器執行,這是職責鏈實現方法,減少職責鏈本身與處理器邏輯之間的耦合,規範了流程處理;HandlerExecutionChain維護了HandlerInterceptor的集合,可以向其中註冊相應的攔截器。
註意事項和細節
- 1.將請求和處理分開,實現解耦,提高系統的靈活性。
- 2.簡化了對象,使對象不需要知道鏈的結構。
- 3.性能會受到影響,特別是在鏈比較長的時候。因此需控制鏈中最大節點數量,一般通過在Handler中設置一個最大節點數,在setNext()方法中,判斷是否已經超過了閥值,超過則不允許該鏈建立,避免出現超長鏈無意識的破壞系統性能。
- 4.調式不方便,採用了遞歸的方式,調試時邏輯可能比較複雜。
- 5.最佳應用場景,有多個對象可以處理同一請求時,比如多級請求,請假/加薪等審批流程,JavaWeb中Tomact對Encoding的處理,攔截器。
只是為了記錄自己的學習歷程,且本人水平有限,不對之處,請指正。