一吟秩、什么是職責鏈模式
從文字角度出發(fā),我們可以先將關(guān)注點放在“鏈”字上绽淘,很容易聯(lián)想到鏈式結(jié)構(gòu)涵防,舉個生活中常見的例子,擊鼓傳花游戲就是一個很典型的鏈式結(jié)構(gòu)沪铭,所有人形成一條鏈壮池,相互傳遞。而從另一個角度說杀怠,職責鏈就是所謂的多級結(jié)構(gòu)椰憋,比如去醫(yī)院開具病假條,普通醫(yī)生只能開一天的證明赔退,如果需要更多時常橙依,則需將開具職責轉(zhuǎn)交到上級去,上級醫(yī)師只能開三天證明硕旗,如需更多時常窗骑,則需將職責轉(zhuǎn)交到他的上級,以此類推漆枚,這就是一個職責鏈模式的典型應(yīng)用创译。再比如公司請假,根據(jù)請假時常的不同墙基,需要遞交到的級別也不同软族,這種層級遞進的關(guān)系就是一種多級結(jié)構(gòu)刷喜。
職責鏈模式(Chain Of Responsibility),使多個對象都有機會處理請求立砸,從而避免請求的發(fā)送者和接收者之間的耦合關(guān)系掖疮。將這個對象連成一條鏈,并沿著這條鏈傳遞該請求仰禽,直到有一個對象處理它為止氮墨。UML結(jié)構(gòu)圖如下:
其中,Handler是抽象處理者吐葵,定義了一個處理請求的接口;ConcreteHandler是具體處理者桥氏,處理它所負責的請求温峭,可訪問它的后繼者,如果可處理該請求就處理字支,否則就將該請求轉(zhuǎn)發(fā)給它的后繼者凤藏。
1. 抽象處理者
抽象處理者實現(xiàn)了三個職責:
- 定義一個請求的處理方法handlerMessage(),是唯一對外開放的方法
- 定義一個鏈的編排方式setNext()堕伪,用于設(shè)置下一個處理者
- 定義了具體的請求者必須實現(xiàn)的兩個方法揖庄,即定義自己能夠處理的級別的getHandlerLevel()方法及具體的處理任務(wù)echo()方法
public abstract class Handler {
private Handler nextHandler; //下一個處理者
public final Response handlerMessage(Request request) {
Response response = null;
if(this.getHandlerLevel().equals(request.getRequestLevel())) { //判斷是否是自己的處理級別
response = this.echo(request);
} else {
if(this.nextHandler != null) { //下一處理者不為空
response = this.nextHandler.handlerMessage(request);
} else {
//沒有適當?shù)奶幚碚撸瑯I(yè)務(wù)自行處理
}
}
return response;
}
//設(shè)定下一個處理者
public void setNext(Handler handler) {
this.nextHandler = handler;
}
//每個處理者的處理等級
protected abstract Level getHandlerLevel();
//每個處理者都必須實現(xiàn)的處理任務(wù)
protected abstract Response echo(Request request);
}
2. 具體處理者
這里我們定義三個具體處理者欠雌,以便能組成一條鏈蹄梢,ConcreteHandlerB及ConcreteHandlerC就不再贅述了。
public class ConcreteHandlerA extends Handler {
@Override
protected Level getHandlerLevel() {
//設(shè)置自己的處理級別
return null;
}
@Override
protected Response echo(Request request) {
//完成處理邏輯
return null;
}
}
3. Level類
Level類負責定義請求和處理級別富俄,具體內(nèi)容需根據(jù)業(yè)務(wù)產(chǎn)生禁炒。
public class Level {
//定義一個請求和處理等級
}
4. Request類
Request類負責封裝請求,具體內(nèi)容需根據(jù)業(yè)務(wù)產(chǎn)生霍比。
public class Request {
//請求的等級
public Level getRequestLevel() {
return null;
}
}
5. Response類
Response類負責封裝鏈中返回的結(jié)果幕袱,具體內(nèi)容需根據(jù)業(yè)務(wù)產(chǎn)生。
public class Response {
//處理者返回的數(shù)據(jù)
}
6. Client客戶端
我們在場景類或高層模塊中對類進行組裝悠瞬,并傳遞請求们豌,返回結(jié)果。如下對三個具體處理者進行組裝浅妆,按照1→2→3的順序望迎,并得出返回結(jié)果。
public class Client {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandlerA();
Handler handler2 = new ConcreteHandlerB();
Handler handler3 = new ConcreteHandlerC();
//設(shè)置鏈中的階段順序 1->2->3
handler1.setNext(handler2);
handler2.setNext(handler3);
//提交請求返回結(jié)果
Response response = handler1.handlerMessage(new Request());
}
}
當然這是個未完成的模板狂打,最終結(jié)果會因為 request.getRequestLevel() 為空而拋出異常擂煞,具體內(nèi)容需根據(jù)業(yè)務(wù)邏輯進行編寫。
二趴乡、職責鏈模式的應(yīng)用
1. 何時使用
- 處理消息時
2. 方法
- 攔截的類都實現(xiàn)同一接口
3. 優(yōu)點
- 將請求和處理分開对省,實現(xiàn)解耦蝗拿,提高系統(tǒng)的靈活性
- 簡化了對象,使對象不需要知道鏈的結(jié)構(gòu)
4. 缺點
- 性能會收到影響蒿涎,特別是在鏈比較長的時候
- 調(diào)試不方便哀托。采用了類似遞歸的方式,調(diào)試時邏輯可能比較復(fù)雜
- 不能保證請求一定被接收
5. 使用場景
- 有多個對象可以處理同一個請求
- 在不明確指定接收者的情況下劳秋,向多個對象中的提交請求
- 可動態(tài)指定一組對象處理請求
6. 應(yīng)用實例
- 多級請求
- 擊鼓傳花
- 請假/加薪請求
- Java Web中Tomcat對Encoding的處理仓手、攔截器
7. 注意事項
- 需控制鏈中最大節(jié)點數(shù)量,一般通過在Handler中設(shè)置一個最大節(jié)點數(shù)量玻淑,在setNext()方法中判斷是否已經(jīng)超過閥值嗽冒,超過則不允許該鏈建立,避免出現(xiàn)超長鏈無意識地破壞系統(tǒng)性能
三补履、職責鏈模式的實現(xiàn)
我們就以請假/加薪為例添坊,實現(xiàn)一個較為簡單的職責鏈模式。UML圖如下:
1. 抽象管理者
通過Manager抽象類管理所有管理者箫锤,setSuperior()方法用于定義職責鏈的下一級贬蛙,即定義當前管理者的上級。
public abstract class Manager {
protected String name;
protected Manager superior; //管理者的上級
public Manager(String name) {
this.name = name;
}
//設(shè)置管理者的上級
public void setSuperior(Manager superior) {
this.superior = superior;
}
//申請請求
public abstract void handlerRequest(Request request);
}
2. 具體管理者
經(jīng)理類如下谚攒,只可批準兩天以內(nèi)的假期阳准,其余請求將繼續(xù)申請上級。
public class CommonManager extends Manager {
public CommonManager(String name) {
super(name);
}
@Override
public void handlerRequest(Request request) {
if (request.getRequestType().equals("請假") && request.getNumber() <= 2) { //只能批準兩天內(nèi)的假期
System.out.println(name + ":" + request.getRequestContent() + "馏臭,時長:" + request.getNumber() + "天野蝇,被批準");
} else { //其余請求申請上級
if (superior != null) {
superior.handlerRequest(request);
}
}
}
}
總監(jiān)類如下,只可批準五天以內(nèi)的假期位喂,其余請求將繼續(xù)申請上級浪耘。
public class Majordomo extends Manager {
public Majordomo(String name) {
super(name);
}
@Override
public void handlerRequest(Request request) {
if (request.getRequestType().equals("請假") && request.getNumber() <= 5) { //只能批準五天內(nèi)的假期
System.out.println(name + ":" + request.getRequestContent() + ",時長:" + request.getNumber() + "天塑崖,被批準");
} else { //其余請求申請上級
if (superior != null) {
superior.handlerRequest(request);
}
}
}
}
總經(jīng)理類七冲,可以批準任意時常的假期,并且可以批準是否加薪规婆。
public class GeneralManager extends Manager {
public GeneralManager(String name) {
super(name);
}
@Override
public void handlerRequest(Request request) {
if (request.getRequestType().equals("請假")) { //能批準任意時長的假期
System.out.println(name + ":" + request.getRequestContent() + "澜躺,時長:" + request.getNumber() + "天,被批準");
} else if (request.getRequestType().equals("加薪") && request.getNumber() <= 500) {
System.out.println(name + ":" + request.getRequestContent() + "抒蚜,金額:¥" + request.getNumber() + "掘鄙,被批準");
} else if (request.getRequestType().equals("加薪") && request.getNumber() > 500) {
System.out.println(name + ":" + request.getRequestContent() + ",金額:¥" + request.getNumber() + "嗡髓,再說吧");
}
}
}
3. 申請類
public class Request {
private String requestType; //申請類別
private String requestContent; //申請內(nèi)容
private int number; //數(shù)量
public String getRequestType() {
return requestType;
}
public void setRequestType(String requestType) {
this.requestType = requestType;
}
public String getRequestContent() {
return requestContent;
}
public void setRequestContent(String requestContent) {
this.requestContent = requestContent;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
4. Client客戶端
下面測試幾組數(shù)據(jù)操漠。
public class Client {
public static void main(String[] args) {
CommonManager commonManager = new CommonManager("尼古拉斯·經(jīng)理");
Majordomo majordomo = new Majordomo("尼古拉斯·總監(jiān)");
GeneralManager generalManager = new GeneralManager("尼古拉斯·總經(jīng)理");
//設(shè)置上級
commonManager.setSuperior(majordomo);
majordomo.setSuperior(generalManager);
Request request = new Request();
request.setRequestType("請假");
request.setRequestContent("adam請假");
request.setNumber(1);
commonManager.handlerRequest(request);
Request request2 = new Request();
request2.setRequestType("請假");
request2.setRequestContent("adam請假");
request2.setNumber(4);
commonManager.handlerRequest(request2);
Request request3 = new Request();
request3.setRequestType("加薪");
request3.setRequestContent("adam請求加薪");
request3.setNumber(500);
commonManager.handlerRequest(request3);
Request request4 = new Request();
request4.setRequestType("加薪");
request4.setRequestContent("adam請求加薪");
request4.setNumber(1000);
commonManager.handlerRequest(request4);
}
}
運行結(jié)果如下: