責任鏈模式:Chain of Responsibility

簡介

職責鏈是一種行爲設計模式,容許你將請求沿着處理者鏈進行發送。收到請求後,每一個處理者都可對請求進行處理,或將其傳遞給鏈上的下個處理者。 java

在這種模式中,一般每一個接收者都包含對另外一個接收者的引用。若是一個對象不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推,直到有對象處理它爲止(也可能沒有),如此使得每一個接收者都有機會接收和處理請求。設計模式

對於調用者來講,調用者只須要將請求發送到職責鏈上便可,無須關心請求的處理細節和請求的傳遞,因此責任鏈也將請求的發送者和處理者進行了解耦。緩存

場景舉例

一個事件須要通過多個對象處理是一個挺常見的場景,譬如客服諮詢流程,採購審批流程,請假流程,軟件開發中的異常處理流程,過濾系統等各類各樣的流程,能夠考慮使用責任鏈模式來實現。 cookie

如圖,假如你剛購置了一個新的硬件設備,可是鏈接上電腦並不能正確執行,因而你決定撥打技術支持電話。網絡

首先你會聽到自動回覆器的機器合成語音,它提供了針對各類問題的九個經常使用解決方案,你按照提示操做,機器人將你轉接到一位人工客服人員。dom

客服發現問題的技術性比較強,沒法提供任何具體的解決方案。因而他將你轉接到一位技術工程師。ide

最後,工程師告訴了你新硬件設備驅動程序的下載網址,以及操做步驟,你按照說明解決了問題,幾經周折,終於大功告成。 函數

概念

  1. 處理者 Handler:聲明瞭全部具體處理者的通用接口。該接口一般包含用於請求處理的方法,以及設置下一個處理者的方法。
  2. 基礎處理者(Base Handler):一般狀況下,該類中定義了一個保存對於下個處理者引用的成員變量。客戶端可經過將處理者傳遞給下個處理者的構造函數或設定方法來建立鏈。該類還能夠實現默認的處理行爲:肯定下個處理者存在後再將請求傳遞給它。
  3. 具體處理者(Concrete Handlers):包含處理請求的實際代碼。每一個處理者接收到請求後,都必須決定是否進行處理,以及是否沿着鏈傳遞請求。處理者一般是獨立且不可變的,須要經過構造函數一次性地得到全部必要地數據。
  4. 客戶端(Client)可根據程序邏輯一次性或者動態地生成鏈。值得注意的是,請求可發送給鏈上的任意一個處理者,而非必須是第一個處理者。

活學活用

以請假流程爲例,通常公司普通員工的請假流程簡化以下 ui

普通員工發起一個請假申請,當請假天數小於3天時主管便可審批;當請假天數大於3天時,須要提交給項目經理審批,但若請假天數大於7天,就要提交給總經理審批。

public interface Handler {
    /** * 處理請假 * * @param days 請假天數 * @return 是否批准 */
    boolean handleHoliday(int days);

    /** * 設置責任鏈的下一個處理者 * * @param h 下一個處理者 */
    void setNext(Handler h);
}

public class BaseHandler implements Handler {
    private Handler next;

    @Override
    public boolean handleHoliday(int days) {
        if (next != null) {
            return next.handleHoliday(days);
        } else {
            System.out.println("未批准");
            return false;
        }
    }

    @Override
    public void setNext(Handler h) {
        next = h;
    }
}

public class LeaderManager extends BaseHandler {
    @Override
    public boolean handleHoliday(int days) {
        if (days <= 3) {
            System.out.println("LeaderManager 批准");
            return true;
        } else {
            return super.handleHoliday(days);
        }
    }
}

public class ProjectManager extends BaseHandler {
    @Override
    public boolean handleHoliday(int days) {
        if (days <= 7) {
            System.out.println("ProjectManager 批准");
            return true;
        } else {
            return super.handleHoliday(days);
        }
    }
}

public class DeptManager extends BaseHandler {
    @Override
    public boolean handleHoliday(int days) {
        System.out.println("DeptManager 批准");
        return true;
    }
}

public class Client {
    public static void main(String[] args) {
        Handler leaderManager = new LeaderManager();
        Handler projectManager = new ProjectManager();
        Handler deptManager = new DeptManager();
        leaderManager.setNext(projectManager);
        projectManager.setNext(deptManager);
        leaderManager.handleHoliday(5);
    }
}

// Logcat:
// ProjectManager 批准
複製代碼

真實例子

Android 事件傳遞機制this

  • 僞代碼
public boolean dispatchTouchEvent(MotionEvent event){
    boolean consume = false;
    if (onInterceptTouchEvent(event)) {
        consume = onTouchEvent(event);
    } else {
        consume = child.dispatchTouchEvent(event);
    }
    return consume;
}
複製代碼

使用場景

  • 當必須按順序執行多個處理者時,可使用該模式。
    • 不管你以何種順序將處理者鏈接成一條鏈,全部請求都會嚴格按照順序經過鏈上的處理者。
  • 若是所需處理者及其順序須要在運行時進行改變,也可使用職責鏈模式。
    • 調用者能夠根據運行時環境,動態地插入和移除處理者,或者改變其順序。

純的與不純的責任鏈模式

「一個純的責任鏈模式要求一個具體的處理者對象只能在兩個行爲中選擇一個:一是承擔責任,而是把責任推給下家。不容許出現某一個具體處理者對象在承擔了一部分責任後又把責任向下傳的狀況。

在一個純的責任鏈模式裏面,一個請求必須被某一個處理者對象所接收;在一個不純的責任鏈模式裏面,一個請求能夠最終不被任何接收端對象所接收。

純的責任鏈模式的實際例子很難找到,通常看到的例子均是不純的責任鏈模式的實現。有些人認爲不純的責任鏈根本不是責任鏈模式,這也許是有道理的。可是在實際的系統裏,純的責任鏈很難找到。若是堅持責任鏈不純便不是責任鏈模式,那麼責任鏈模式便不會有太大意義了。 」

優缺點

  • 優勢
    • 可控的請求處理的順序。
    • 單一職責原則:每一個處理者職責單一,而且對發起操做和執行操做的類進行了解耦。
    • 開閉原則:你能夠在不更改現有代碼的狀況下在程序中新增處理者。
  • 缺點
    • 有可能出現請求不被任何處理者處理的狀況;
    • 責任鏈創建不當,有可能出現死循環;

和其餘設計模式比較

  • 常見組合
    • 責任鏈一般和組合模式結合使用。在這種狀況下,父組件接收到請求後,能夠將請求沿包含子組件的鏈一直傳遞至對象樹的葉結點。 Android 中 View 及其時間傳遞機制正是如此;
  • 容易混淆
    • 責任鏈模式與命令模式
      • 責任鏈按照順序將請求動態傳遞給一系列的潛在接收者,直至其中一名接收者對請求進行處理。
      • 命令模式在發送者和請求者之間創建單向鏈接。
    • 責任鏈和裝飾模式
      • 裝飾模式主要目的是給原對象增長新行爲,責任鏈的主要目的是讓多個處理者都有機會處理某個請求;
      • 責任鏈的管理者能夠相互獨立地執行一切操做,還能夠隨時中止傳遞請求;
      • 能夠在遵循基本接口的狀況下擴展對象的行爲;
      • 裝飾沒法中斷請求的傳遞。

項目中例子

  • Router?

  • OkHttp Interceptor

    • 在OkHttp的內部實現中,interceptors並不只僅是攔截器這麼簡單。實際上,OkHttp發送網絡請求的一切核心功能,包括創建鏈接、發送請求、讀取緩存等,都是經過interceptors來實現的,這些interceptors在運行的時候彼此協做,構成了一個interceptor chain。
/** * 項目中自定義的 Interceptor */
object DomainSwitchInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        val requestDomain = request.url().host()
        val domainManager = DomainManagerFactory.getInstance().getDomainManagerByDomain(requestDomain)
        val currentDomain = domainManager?.pickHostWithoutSchema()
        if (currentDomain != null && requestDomain != currentDomain) {
            request = request.newBuilder()
                .url(request.url().newBuilder().host(currentDomain).build())
                .build()
        }
        return chain.proceed(request)
    }
}


/** * 將自定義 Interceptor 添加到 OkHttp * 最終添加到 OkHttpClient 靜態內部類 Builder 的 final List<Interceptor> interceptors 中 */
object RetrofitRestClient {

    val defaultOkHttpClient: OkHttpClient by lazy {
        VolleyManager.getOkHttpClient()
            .newBuilder()
            .addInterceptor(RequestCommonParamsInterceptor)
            .addInterceptor(DomainSwitchInterceptor)
            .addInterceptor(RequestMonitorInterceptor)
            .dns(HttpDns)
            .build()
    }
}

/** * 全部請求都是從 OkHttp 的 RealCall 的 getResponseWithInterceptorChain() 方法開始 */
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors()); // client.interceptors()包含了自定義的攔截器
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    // 將全部 Interceptor 放入 RealInterceptorChain 構造責任鏈
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }
  
  /** * OkHttp Interceptor */
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  /** * 構建責任鏈的接口 */
  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    @Nullable Connection connection();

    Call call();

    int connectTimeoutMillis();

    Chain withConnectTimeout(int timeout, TimeUnit unit);

    int readTimeoutMillis();

    Chain withReadTimeout(int timeout, TimeUnit unit);

    int writeTimeoutMillis();

    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }
}

/** * Chain 的實現類 RealInterceptorChain 的 proceed 方法 */
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    return response;
  }
複製代碼

Article by Panxc

相關文章
相關標籤/搜索