關注公衆號 JavaStorm 便於隨時閱讀接收最新干貨java
責任鏈模式(Chain of Responsibility Pattern):避免請求發送者與接收者耦合在一塊兒,讓多個對象都有可能接收請求,將這些對象鏈接成一條鏈,而且沿着這條鏈傳遞請求,直到有對象處理它爲止。職責鏈模式是一種對象行爲型模式。算法
用通俗的話就是推卸責任某件事,咱們去解決,找到A,結果A踢皮球,說這不關個人事,去找B解決,而後咱們就去找B,結果B也說,這跟我不要緊,快去找C,就這樣,咱們就被踢來踢去,這就是責任鏈模式的思想。設計模式
Handler(抽象處理者):它定義了處理請求的接口、通常設計成抽象類,持有一個處理器的引用,若是本身沒法處理請求,則轉給下一個處理器。所以在抽象處理者中定義了一個抽象處理者類型的對象,做爲其對下家的引用。經過該引用,處理者能夠連成一條鏈。bash
ConcreteHandler(具體處理者):它是抽象處理者的子類,能夠處理用戶請求,在具體處理者類中實現了抽象處理者中定義的抽象請求處理方法,在處理請求以前須要進行判斷,看是否有相應的處理權限,若是能夠處理請求就處理它,不然將請求轉發給後繼者;在具體處理者中能夠訪問鏈中下一個對象,以便請求的轉發。ide
如今模擬一個場景請求,當數字符合奇數、指定數字、某個下限等可處理,不然就處理失敗。性能
首先咱們定義抽象處理器,以及處理方法模板,定義了當前請求哪些能夠處理,不能處理就轉給next。this
package com.zero.headfirst.chain.support;
import java.util.Objects;
/** * 抽象處理器:定義了抽象請求處理方法,持有一個抽象處理器的引用,用於生成一條處理鏈 */
public abstract class AbstractSupport {
private String name;
private AbstractSupport next;
/** * 定義處理請求的算法模板 */
public final void process(NumberData numberData) {
if (resolve(numberData)) {
done(numberData);
} else if (Objects.nonNull(next)) {
next.process(numberData);
} else {
fail(numberData);
}
}
/** * 沒法處理 * @param numberData */
protected void fail(NumberData numberData) {
System.out.println(numberData + " cannot be resolved.");
}
/** * 成功處理 * @param numberData */
protected void done(NumberData numberData) {
System.out.println(numberData + " is resolved by " + this + ".");
}
/** * 判斷是否能處理,子類重寫 * @param numberData * @return */
protected abstract boolean resolve(NumberData numberData);
public AbstractSupport setNext(AbstractSupport next) {
this.next = next;
return next;
}
public AbstractSupport(String name) {
this.name = name;
}
@Override
public String toString() {
return "AbstractSupport{" +
"name='" + name + '\'' +
'}';
}
}
複製代碼
而後定義咱們的具體處理器,NoSupport
是一個永遠不解決,只管推卸責任的類。spa
package com.zero.headfirst.chain.support;
/** * 永遠不解決,返回false */
public class NoSupport extends AbstractSupport {
public NoSupport(String name) {
super(name);
}
@Override
protected boolean resolve(NumberData numberData) {
return false;
}
}
複製代碼
LimitSupport
解決指定範圍的請求設計
package com.zero.headfirst.chain.support;
/** * 解決指定區間 */
public class LimitSupport extends AbstractSupport {
private Integer limit;
public LimitSupport(String name, Integer limit) {
super(name);
this.limit = limit;
}
@Override
protected boolean resolve(NumberData numberData) {
return numberData.getNumber() < limit;
}
}
複製代碼
OddSupport
類,解決奇數的問題調試
package com.zero.headfirst.chain.support;
/** * 奇數處理 */
public class OddSupport extends AbstractSupport {
public OddSupport(String name) {
super(name);
}
@Override
protected boolean resolve(NumberData numberData) {
return numberData.getNumber() % 2 == 1;
}
}
複製代碼
SpecialSupport
指定數字處理器
package com.zero.headfirst.chain.support;
/** * 指定數字特殊處理 */
public class SpecialSupport extends AbstractSupport {
private Integer number;
public SpecialSupport(String name, Integer number) {
super(name);
this.number = number;
}
@Override
protected boolean resolve(NumberData numberData) {
return numberData.getNumber().equals(number);
}
}
複製代碼
最後咱們定義一個客戶端去組裝咱們的責任鏈,將全部的處理器連在一塊兒,而且模擬請求
package com.zero.headfirst.chain.support;
public class ChainTest {
public static void main(String[] args) {
AbstractSupport alice = new NoSupport("Alice");
AbstractSupport jams = new OddSupport("jams");
AbstractSupport bob = new LimitSupport("Bob", 100);
AbstractSupport charlie = new SpecialSupport("Charlie", 429);
//定義責任鏈
alice.setNext(jams).setNext(bob).setNext(charlie);
for (int i = 0; i < 500; i += 33) {
alice.process(new NumberData(i));
}
}
}
複製代碼
咱們的打印結果:除了咱們定義處理器能處理的數字能夠處理完成,其餘數字沒法處理。
NumberData{number=0} is resolved by AbstractSupport{name='Bob'}.
NumberData{number=33} is resolved by AbstractSupport{name='jams'}.
NumberData{number=66} is resolved by AbstractSupport{name='Bob'}.
NumberData{number=99} is resolved by AbstractSupport{name='jams'}.
NumberData{number=132} cannot be resolved.
NumberData{number=165} is resolved by AbstractSupport{name='jams'}.
NumberData{number=198} cannot be resolved.
NumberData{number=231} is resolved by AbstractSupport{name='jams'}.
NumberData{number=264} cannot be resolved.
NumberData{number=297} is resolved by AbstractSupport{name='jams'}.
NumberData{number=330} cannot be resolved.
NumberData{number=363} is resolved by AbstractSupport{name='jams'}.
NumberData{number=396} cannot be resolved.
NumberData{number=429} is resolved by AbstractSupport{name='jams'}.
NumberData{number=462} cannot be resolved.
NumberData{number=495} is resolved by AbstractSupport{name='jams'}.
複製代碼
好比說公司請假須要審批,舉個不恰當的例子,若是請假小於3天,主管審批;4-10天的, 經理審批;11-30天的,總經理審批;超過30天的,不批准等等。這就得一步步去判斷, 若是撇開設計模式不看的話,那麼咱們可使用if…else…把它解決了,可是問題可想而知, 實際中的複雜程度時遠遠超過這個例子的。
如今咱們用責任鏈設計模式解決,將每種角色鏈接起來處理請假數據流。
package com.zero.design.actions.dutychain;
/** * 責任鏈抽象類,包含核心處理方法,以及後繼責任處理器設置.由不一樣的處理器繼承 */
public abstract class AbstractLeaderHandler {
protected String handlerName;
/** * 責任鏈上的後繼對象,即這個對象沒法處理,就轉移給下一個Leader */
protected AbstractLeaderHandler nextLeaderHandler;
public AbstractLeaderHandler(String handlerName) {
this.handlerName = handlerName;
}
/** * 處理請求的核心的業務方法 * 須要不一樣繼承該類的處理器本身實現 * @param request */
public abstract void handleRequest(LeaveRequest request);
/** * 設定責任鏈上的後繼對象 * @param nextLeaderHandler */
public AbstractLeaderHandler setNextLeaderHandler(AbstractLeaderHandler nextLeaderHandler) {
this.nextLeaderHandler = nextLeaderHandler;
return nextLeaderHandler;
}
}
複製代碼
負責處理 3 天如下的假期,大於三天的遞交給主管
package com.zero.design.actions.dutychain;
/** * 主任處理器,繼承抽象處理器接口,處理相應業務或設置下一個處理器 */
public class DirectorHandler extends AbstractLeaderHandler {
public DirectorHandler(String handlerName) {
super(handlerName);
}
@Override
public void handleRequest(LeaveRequest request) {
int days = request.getLeaveDays(); //獲取請假天數
String name = request.getName(); //獲取請假人姓名
String reason = request.getReason(); // 獲取請假理由
if (days <= 3) {
//若是知足3天內的要求,主任直接審批
System.out.println("員工" + name + "請假" + days + "天,理由:" + reason);
System.out.println("主任" + this.handlerName + "審批經過");
} else {
System.out.println("請假天數過多,主任" + this.handlerName + "處理結束,還要交給經理處理");
if(this.nextLeaderHandler != null) {
//不然,若是鏈上存在下一個Leader,就讓他處理
this.nextLeaderHandler.handleRequest(request);
}
}
}
}
複製代碼
處理小於10天的假期,大於10則遞交給總辦
package com.zero.design.actions.dutychain;
/** * 經理處理器 */
public class ManagerHandler extends AbstractLeaderHandler {
public ManagerHandler(String handlerName) {
super(handlerName);
}
@Override
public void handleRequest(LeaveRequest request) {
int days = request.getLeaveDays(); //獲取請假天數
String name = request.getName(); //獲取請假人姓名
String reason = request.getReason(); // 獲取請假理由
if(days <= 10) { //若是知足10天內的要求,經理直接審批
System.out.println("員工" + name + "請假" + days + "天,理由:" + reason);
System.out.println("經理" + this.handlerName + "審批經過");
} else {
System.out.println("請假天數過多,經理" + this.handlerName + "處理結束,交給總辦處理");
if(this.nextLeaderHandler != null) { //不然,若是鏈上存在下一個Leader,就讓他處理
this.nextLeaderHandler.handleRequest(request);
}
}
}
}
複製代碼
處理大於 10 天假期的請求
package com.zero.design.actions.dutychain;
/** * 總辦處理器 */
public class GeneralManagerHandler extends AbstractLeaderHandler {
public GeneralManagerHandler(String handlerName) {
super(handlerName);
}
@Override
public void handleRequest(LeaveRequest request) {
int days = request.getLeaveDays(); //獲取請假天數
String name = request.getName(); //獲取請假人姓名
String reason = request.getReason(); // 獲取請假理由
if(days <= 30) { //若是知足10天內的要求,經理直接審批
System.out.println("員工" + name + "請假" + days + "天,理由:" + reason);
System.out.println("總辦" + this.handlerName + "審批經過");
} else {
System.out.println("請假天數過多,總辦" + this.handlerName + "無法處理");
if(this.nextLeaderHandler != null) { //不然,若是鏈上存在下一個Leader,就讓他處理
this.nextLeaderHandler.handleRequest(request);
} else {
System.out.println("請假不成功");
}
}
}
}
複製代碼
聲明一條處理鏈,模擬請假數據流
package com.zero.design.actions.dutychain;
/** * 模擬請求: * 他們處理請求的條件是不一樣的,並且只要不是本身處理範圍以內的,就會將請求傳給鏈上的下一位來處理 */
public class Client {
public static void main(String[] args) {
//初始化責任鏈,應該使用單例
AbstractLeaderHandler directorHandler = new DirectorHandler("張勝男");
AbstractLeaderHandler managerHandler = new ManagerHandler("李四");
AbstractLeaderHandler generalManagerHndler = new GeneralManagerHandler("趙王");
//組織好責任對象關係
directorHandler.setNextLeaderHandler(managerHandler).setNextLeaderHandler(generalManagerHndler);
//開始請假操做
LeaveRequest request = new LeaveRequest("倪升武", 15, "在家睡覺");
directorHandler.handleRequest(request);
}
}
複製代碼
最後咱們看打印結果
請假天數過多,主任張勝男處理結束,還要交給經理處理
請假天數過多,經理李四處理結束,交給總辦處理
員工倪升武請假15天,理由:在家睡覺
總辦趙王審批經過
複製代碼
Pipeline
和 ChannelHandler
經過責任鏈設計模式來組織代碼邏輯