設計模式之職責鏈
DP中對職責鏈模式的定義稍微不是那麽的好理解,簡單來說就是將能夠處理用戶請求的對象都串成一條鏈,然後將用戶的請求放進這條鏈裏,這個請求就可以在鏈中的對象之間傳遞,一直傳遞到能夠處理它的對象上為止。
可能這麽說還不太好理解,我們可以舉個生活中的例子,例如員工申請加薪。我們都知道普通員工想要申請加薪,首先需要將加薪的申請書提交給主管,如果主管沒有權限處理這個加薪的事情,那麽主管就會將申請提交到部門經理那邊,由部門經理去處理。如果部門經理也沒有權處理的話,就會將申請再提交到總經理或人力資源總監手上,總經理說我可以處理,那麽這個加薪申請就可以被處理了。而普通員工並不知道他提交的這個加薪申請被誰處理了,也不知道處理的經過,他只需要知道將申請提交給主管,最後等結果即可。這就是一個典型的職責鏈機制:客戶端發送請求到職責鏈的鏈頭上,然後這個請求就會在鏈中的對象之間逐個傳遞,直到被某個對象處理並返回結果給客戶端為止。
示意圖:
不使用職責連模式設計的代碼:
就拿以上所說到的例子,我們來用代碼做一個簡單的試驗,先不使用職責鏈模式,就用最簡單方式去實現這個場景。
1.編寫一個RaisesRequest類,用來封裝請求內容:
package org.zero01.test; public class RaisesRequest { public String getRequestName() { return requestName; } public void setRequestName(String requestName) { this.requestName = requestName; } public int getRequestNumber() { return requestNumber; } public void setRequestNumber(int requestNumber) { this.requestNumber = requestNumber; } private String requestName; private int requestNumber; }
2.編寫管理者類Manager,這個類用於處理用戶的請求:
package org.zero01.test;
import org.zero01.test.RaisesRequest;
public class Manager {
// 返回處理結果
public void getResult(String position, RaisesRequest raisesRequest) {
if (position.equals("主管")) {
if (raisesRequest.getRequestName().equals("加薪")) {
System.out.println("主管:我無權處理");
} else {
System.out.println("主管:你在說啥");
}
} else if (position.equals("部門經理")) {
if (raisesRequest.getRequestName().equals("加薪")) {
System.out.println("部門經理:我無權處理");
} else {
System.out.println("部門經理:你在說啥");
}
} else if (position.equals("人力資源總監")) {
if (raisesRequest.getRequestName().equals("加薪")) {
System.out.println("人力資源總監:我無權處理");
} else {
System.out.println("人力資源總監:你在說啥");
}
} else if (position.equals("總經理")) {
if (raisesRequest.getRequestName().equals("加薪") && raisesRequest.getRequestNumber() <= 1000) {
System.out.println("總經理:ok,批準了");
} else if(raisesRequest.getRequestName().equals("加薪") && raisesRequest.getRequestNumber() > 1000){
System.out.println("總經理:emmm我考慮一下");
}else{
System.out.println("總經理:你在說啥");
}
} else {
System.out.println("沒有這個職位");
}
}
}
3.最後編寫客戶端類Client:
package org.zero01.test;
public class Client {
public static void main(String[] args) {
// 封裝請求內容
RaisesRequest raisesRequest = new RaisesRequest();
raisesRequest.setRequestName("加薪");
raisesRequest.setRequestNumber(1000);
// 發送給不同的管理者
Manager manager = new Manager();
manager.getResult("主管", raisesRequest);
manager.getResult("部門經理", raisesRequest);
manager.getResult("人力資源總監", raisesRequest);
manager.getResult("總經理", raisesRequest);
}
}
運行結果:
主管:我無權處理
部門經理:我無權處理
人力資源總監:我無權處理
總經理:ok,批準了
ok,從運行結果也可以看到,這樣的代碼也是勉勉強強實現了我們的需求。但是,很直觀的可以看到代碼是有多糟糕,Manager類裏的if else全都集中在一個方法裏,造成一個方法裏重復的代碼太多。而且不同部門的管理者都在這個方法裏,這就違反了單一職責原則,如果我要增加一個部門的管理者或減少一個部門的管理者,都需要去修改這個類裏的代碼以及客戶端的代碼,違反了開-閉原則。這樣的代碼耦合性是相當高的,但是我們要怎麽樣對這些代碼進行解耦呢?那麽這時候就需要使用到職責鏈模式了。
職責鏈模式結構圖:
職責連模式示例代碼:
1.Handler類,定義一個處理請求的接口:
package org.zero01.test;
public abstract class Handler {
protected Handler successor;
// 設置繼任者
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void handlerRequest(int request);
}
2.ConcreteHandler1類,是一個具體的處理類,它只能處理它所負責的請求,不能處理的就傳遞給它的後繼者處理:
package org.zero01.test;
public class ConcreteHandler1 extends Handler {
public void handlerRequest(int request) {
// 處理0-10之間的請求
if (request >= 0 && request <= 10) {
System.out.println("ConcreteHandler1處理請求" + request);
} else if (successor != null) {
// 將請求傳遞到下一位
successor.handlerRequest(request);
}
}
}
3.ConcreteHandler2:
package org.zero01.test;
public class ConcreteHandler2 extends Handler {
public void handlerRequest(int request) {
// 處理10-20之間的請求
if (request >= 10 && request <= 20) {
System.out.println("ConcreteHandler2處理請求" + request);
} else if (successor != null) {
// 將請求傳遞到下一位
successor.handlerRequest(request);
}
}
}
4.ConcreteHandler3:
package org.zero01.test;
public class ConcreteHandler3 extends Handler {
public void handlerRequest(int request) {
// 處理20-30之間的請求
if (request >= 20 && request <= 30) {
System.out.println("ConcreteHandler3處理請求" + request);
} else if (successor != null) {
// 將請求傳遞到下一位
successor.handlerRequest(request);
}
}
}
5.Client,客戶端類,在該類中向職責鏈提交請求:
package org.zero01.test;
public class Client {
public static void main(String[] args) {
Handler h1 = new ConcreteHandler1();
Handler h2 = new ConcreteHandler2();
Handler h3 = new ConcreteHandler3();
// 設置職責鏈的上家與下家,h1作為鏈頭
h1.setSuccessor(h2);
h2.setSuccessor(h3);
int[] numbers = {10, 20, 30, 51, 25, 5, 8, 7, 15, 47};
for (int i : numbers) {
h1.handlerRequest(i);
}
}
}
運行結果:
ConcreteHandler1處理請求10
ConcreteHandler2處理請求20
ConcreteHandler3處理請求30
ConcreteHandler3處理請求25
ConcreteHandler1處理請求5
ConcreteHandler1處理請求8
ConcreteHandler1處理請求7
ConcreteHandler2處理請求15
職責鏈的好處:
從以上這個小示例可以看到,每當我從客戶端提交一個請求時,請求是沿著職責鏈傳遞的,直至傳遞到有對應的ConcreteHandler對象來處理它。請求的處理著與發送者都不需要知道對方的明確信息,且鏈中的對象自己也並不知道鏈的結構。所以職責鏈可以簡化對象之間的互相連接,它們僅需要保持一個指向其後繼者的引用,而不需要保持它所有的候選者的引用,從這點也可以看出職責鏈是單鏈的結構,這樣大大的降低了類之間的耦合度,因為它們都只依賴於一個抽象父類。
類之間低耦合的好處就是可以隨時增加、減少以及修改某個處理請求的結構代碼,增強了給對象指派職責的靈活性。但是要註意一個請求可能到了鏈的末端都得不到處理,或者因為沒有正確配置而得不到處理。這時候估計就有人註意到以上代碼的一個小細節,數值大於30的請求都沒有被處理,這就是典型的請求被傳遞到了鏈的末端也得不到處理。所以在設計代碼時還需要仔細考慮全面,如果是上面這個例子我們就可以簡單的加一個else分支來處理這個問題,而不同的場景下需要根據實際情況使用相應的方式去處理。
使用職責連模式重構之前的代碼:
我們順便把需求更改一下,讓不同的管理者可以處理不同額度的加薪請求。
代碼結構圖:
1.抽象一個Manager管理類:
package org.zero01.test;
public abstract class Manager {
protected Manager superior;
protected String position;
// 設置管理對象的名稱
public Manager(String position) {
this.position = position;
}
// 設置上級對象
public void setSuperior(Manager superior) {
this.superior = superior;
}
// 處理加薪請求的方法
public abstract void raisesRequest(String requestName,int requestNumber);
}
2.編寫主管類,這是具體的處理類,可以處理100元以內的加薪請求:
package org.zero01.test;
public class Director extends Manager {
public Director(String position) {
super(position);
}
public void raisesRequest(String requestName, int requestNumber) {
// 有權處理100元以內的加薪
if (requestName.equals("加薪") && requestNumber <= 100) {
System.out.println(position + ":ok,批準了");
} else if (superior != null) {
// 將請求傳遞給上級
superior.raisesRequest(requestName, requestNumber);
}
}
}
3.部門經理類,可以處理300元以內的加薪請求:
package org.zero01.test;
public class BranchManager extends Manager{
public BranchManager(String position) {
super(position);
}
public void raisesRequest(String requestName, int requestNumber) {
// 有權處理300元以內的加薪
if (requestName.equals("加薪") && requestNumber <= 300) {
System.out.println(position + ":ok,批準了");
} else if (superior != null) {
// 將請求傳遞給上級
superior.raisesRequest(requestName, requestNumber);
}
}
}
4.人力資源總監類,可以處理600元以內的加薪請求:
package org.zero01.test;
public class CHO extends Manager{
public CHO(String position) {
super(position);
}
public void raisesRequest(String requestName, int requestNumber) {
// 有權處理600元以內的加薪
if (requestName.equals("加薪") && requestNumber <= 600) {
System.out.println(position + ":ok,批準了");
} else if (superior != null) {
// 將請求傳遞給上級
superior.raisesRequest(requestName, requestNumber);
}
}
}
5.總經理類,可以處理1000元以上的加薪請求:
package org.zero01.test;
public class GeneralManager extends Manager{
public GeneralManager(String position) {
super(position);
}
public void raisesRequest(String requestName, int requestNumber) {
// 有權處理1000元以上的加薪
if (requestName.equals("加薪") && requestNumber <= 1000) {
System.out.println(position + ":ok,批準了");
}else {
// 1000元以上看心情
System.out.println(position + ":emmm我考慮一下");
}
}
}
6.員工類,也就是客戶端類:
package org.zero01.test;
public class Staff {
public static void main(String[] args) {
// 實例化各個管理者對象
Manager director = new Director("主管");
Manager branchManager = new BranchManager("部門經理");
Manager cho = new CHO("人力資源總監");
Manager generalManager = new GeneralManager("總經理");
// 設置上級與下級
director.setSuperior(branchManager);
branchManager.setSuperior(cho);
cho.setSuperior(generalManager);
director.raisesRequest("加薪",100);
director.raisesRequest("加薪",300);
director.raisesRequest("加薪",600);
director.raisesRequest("加薪",1000);
director.raisesRequest("加薪",1500);
}
}
運行結果:
主管:ok,批準了
部門經理:ok,批準了
人力資源總監:ok,批準了
總經理:ok,批準了
總經理:emmm我考慮一下
使用職責鏈模式來重構之前的代碼後,就很好的解決了原來大量的分支判斷耦合在一起,造成維護困難、靈活性差且不好擴展的問題。
設計模式之職責鏈