責任鏈模式
顧名思義,責任鏈模式(Chain of Responsibility Pattern)為請求創建了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。這種類型的設計模式屬于行為型模式。
在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個對象不能處理該請求,那么它會把相同的請求傳給下一個接收者,依此類推。
介紹
意圖: 避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,并且沿著這條鏈傳遞請求,直到有對象處理它為止。
主要解決: 職責鏈上的處理者負責處理請求,客戶只需要將請求發送到職責鏈上即可,無須關心請求的處理細節和請求的傳遞,所以職責鏈將請求的發送者和請求的處理者解耦了。
何時使用: 在處理消息的時候以過濾很多道。
如何解決: 攔截的類都實現統一接口。
關鍵代碼: Handler 里面聚合它自己,在 HandlerRequest 里判斷是否合適,如果沒達到條件則向下傳遞,向誰傳遞之前 set 進去。
應用實例:
- JS 中的事件冒泡。
- spring 攔截器。
- JAVA WEB 中 Apache Tomcat 對 Encoding 的處理
- Struts2 的攔截器
優點:
- 降低耦合度。它將請求的發送者和接收者解耦。
- 簡化了對象。使得對象不需要知道鏈的結構。
- 增強給對象指派職責的靈活性。通過改變鏈內的成員或者調動它們的次序,允許動態地新增或者刪除責任。
- 增加新的請求處理類很方便。
缺點:
- 不能保證請求一定被接收。
- 系統性能將受到一定影響,而且在進行代碼調試時不太方便,可能會造成循環調用。
- 可能不容易觀察運行時的特征,有礙于除錯。
使用場景:
- 有多個對象可以處理同一個請求,具體哪個對象處理該請求由運行時刻自動確定。
- 在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。
- 可動態指定一組對象處理請求。
注意事項: 在 JAVA WEB 中遇到很多應用。
主要涉及到以下幾個核心角色:
抽象處理者(Handler):
定義一個處理請求的接口,通常包含一個處理請求的方法(如 handleRequest)和一個指向下一個處理者的引用(后繼者)。
具體處理者(ConcreteHandler):
實現了抽象處理者接口,負責處理請求。如果能夠處理該請求,則直接處理;否則,將請求傳遞給下一個處理者。
客戶端(Client):
創建處理者對象,并將它們連接成一條責任鏈。通常,客戶端只需要將請求發送給責任鏈的第一個處理者,無需關心請求的具體處理過程。
代碼實現
以公司中的OA申請為例,有各種申請:請假、離職、團建、加班、購買設備等;不同申請需要的權限不一樣,需要審批的人不一樣。
定義申請類
/*** 申請*/
public class Petition {/*** 標題*/private String name;/*** 內容*/private String content;private Type type;/*** 申請人*/private String asker;public Petition(String name, String content, String asker, Type type) {this.name = name;this.content = content;this.asker = asker;this.type = type;}public String getName() {return name;}public String getContent() {return content;}public Type getType() {return type;}public String getAsker() {return asker;}public enum Type {/*** 請假*/LEAVE,/*** 加班*/OVERTIME,/*** 離職*/OFF_WORK,/*** 團建*/TRAVEL,/*** 購買設備*/BUY_EQUIPMENT,/*** 其他*/OTHER}
}
定義抽象職位
/*** 職位*/
public abstract class Position {private Position nextPosition;public Position getNextPosition() {return this.nextPosition;}public Position nextPosition(Position nextPosition) {this.nextPosition = nextPosition;return this.nextPosition;}public abstract boolean handle(Petition petition);}
定義小組長,小組長只能審批類型為其他的申請
/*** 小組長*/
public class TeamLeader extends Position{@Overridepublic boolean handle(Petition petition) {if (petition.getType() == Petition.Type.OTHER){System.out.println("小組長審批" + petition.getName() + "通過");return true;}//小組長沒有權限,提交給上一級審批return getNextPosition().handle(petition);}
}
定義經理,經理能夠審批加班,請假,團建的申請
/*** 項目經理*/
public class Manager extends Position{@Overridepublic boolean handle(Petition petition) {if (petition.getType() == Petition.Type.OVERTIME|| petition.getType() == Petition.Type.LEAVE|| petition.getType() == Petition.Type.TRAVEL){System.out.println("項目經理審批" + petition.getName() + "通過");return true;}//項目經理沒有權限,提交給上一級審批return getNextPosition().handle(petition);}
}
定義總經理,能夠審批購買設備,離職的申請
/*** 總經理*/
public class GeneralManager extends Position{@Overridepublic boolean handle(Petition petition) {if (petition.getType() == Petition.Type.BUY_EQUIPMENT||petition.getType() == Petition.Type.OFF_WORK){System.out.println("總經理經理審批" + petition.getName() + "通過");return true;}else {System.out.println("總經理經理審批" + petition.getName() + "不通過");return false;}}
}
客戶端
public class Client {public static void main(String[] args) {Manager manager = new Manager();TeamLeader teamLeader = new TeamLeader();GeneralManager generalManager = new GeneralManager();//小組長上級項目經理,項目經理上級總經理teamLeader.nextPosition(manager).nextPosition(generalManager);teamLeader.handle(new Petition("Buy equipment", "I need to buy a new computer", "Jim", Petition.Type.BUY_EQUIPMENT));teamLeader.handle(new Petition("Leave", "I need to leave", "Jim", Petition.Type.LEAVE));teamLeader.handle(new Petition("Overtime", "I need to work overtime", "Jim", Petition.Type.OVERTIME));teamLeader.handle(new Petition("Off work", "I need to go off work", "Jim", Petition.Type.OFF_WORK));teamLeader.handle(new Petition("Travel", "I need to travel", "Jim", Petition.Type.TRAVEL));teamLeader.handle(new Petition("Other", "I need to do something else", "Jim", Petition.Type.OTHER));}
}