【責任鏈設計模式詳解】C/Java/JS/Go/Python/TS不同語言實現

2023-03-24 21:00:58

簡介

責任鏈模式(Chain of Responsibility Pattern)是一種行為型設計模式,也叫職責鏈模式、命令鏈模式。這種模式為請求建立了一個接收者物件的鏈,允許你將請求沿著處理者鏈進行傳送,每個處理者均可對請求進行處理,或將其傳遞給鏈上的下個處理者。

當程式需要使用不同方式來處理多種類請求,且請求型別和順序不可知,或者當必須按順序執行多個處理時,可以使用責任鏈模式。或者如果所需處理及其順序必須在執行時進行改變,也可以使用該模式。

作用

  1. 避免請求傳送者與接收者耦合在一起,客戶只需要將請求傳送到鏈上,而無須關心請求的處理細節和請求的傳遞。
  2. 通過改變鏈內的成員或者調動它們的次序,允許動態地新增或者刪除責任。

實現步驟

  1. 建立一個抽象處理器類,用來供處理器繼承。
  2. 抽象處理器類可將各子類按任意組織為鏈式,以便呼叫。
  3. 建立多個互不干涉的處理器,實現抽象類的next方法,以便不斷執行鏈式檢查。

UML

 

Java程式碼

抽象事件處理類

// AbstractHandler.java 所有處理變成鏈式,可以互動干涉,動態組合
public abstract class AbstractHandler {
   // 形成職責鏈
   private AbstractHandler next;

   // 建立呼叫鏈,傳入多個handler,按順序形成鏈,返回第一個handler
   public static AbstractHandler link(AbstractHandler first, AbstractHandler... chain) {
      AbstractHandler head = first;
      for (AbstractHandler handler : chain) {
         head.next = handler;
         head = handler;
      }
      return first;
   }

   // 子類需要實現的檢查方法
   public abstract boolean check(int uid);

   // 繼續下一個檢查
   protected boolean checkNext(int uid) {
      if (next == null) {
         return true;
      }
      return next.check(uid);
   }
}

 

不同事件,可以多個,互不關聯

```java
// AuthHandler.java 許可權檢查類
public class AuthHandler extends AbstractHandler {
    // 如果檢查不通過則返回失敗,否則繼續下一個檢查
    public boolean check(int uid) {
      System.out.println(this.getClass().getName() + "::check() [uid = " + uid + "]");
      if (uid % 2 == 0) {
          return false;
      }
      return checkNext(uid);
  }
}
```

```java
// RequestHandler.java 請求是否安全合法檢查
public class RequestHandler extends AbstractHandler {
    // 如果檢查不通過則返回失敗,否則繼續下一個檢查
    public boolean check(int uid) {
      System.out.println(this.getClass().getName() + "::check() [uid = " + uid + "]");
      if (uid % 1 != 0) {
          return false;
      }
      return checkNext(uid);
  }
}
```

```java
// UserHandler.java 使用者基本資訊檢查類
public class UserHandler extends AbstractHandler {
    // 如果檢查不通過則返回失敗,否則繼續下一個檢查
    public boolean check(int uid) {
        System.out.println(this.getClass().getName() + "::check() [uid = " + uid + "]");
        if (uid % 3 == 0) {
            return false;
        }
        return checkNext(uid);
    }
}
```

 

測試呼叫

    /**
     * 責任鏈模式核心是打造一個呼叫處理鏈,每個處理鏈都實現抽象類的next方法,從而可以任意組織各種檢查行為。
     * 通過改變鏈內的成員或者調動它們的順序,允許動態地新增或者刪除職責,從而實現按需組織。
     */

    // 可以任意組織職責鏈,先後順序根據需要來
    AbstractHandler handler1 = AbstractHandler.link(
        new RequestHandler(),
        new UserHandler(),
        new AuthHandler());

    System.out.println("handler1.check(1001)開始");
    handler1.check(1001);
    System.out.println("handler1.check(1002)開始");
    handler1.check(1002);

    // 可以任意組織職責鏈,先後順序根據需要來
    AbstractHandler handler2 = AbstractHandler.link(
        new AuthHandler(),
        new RequestHandler(),
        new UserHandler());

    System.out.println("handler2.check(1001)開始");
    handler2.check(1001);
    System.out.println("handler2.check(1002)開始");
    handler2.check(1002);

 

C語言程式碼

func.h 標頭檔案函數

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// 定義通用handler
typedef struct Handler
{
    char name[50];
    // handler鏈指標
    struct Handler *next;
    // 結構體內部的check_handler函數,供各handler獨立實現
    bool (*check_handler)(struct Handler *, int);
} Handler;

// 建立handler呼叫鏈,逐個建立
Handler *link_handler(Handler *handler, Handler *next);

// 兩種建立鏈式hander的方式,功能相同,可以傳入多個引數
Handler *make_handler_chain_count(int lenght, ...);
Handler *make_handler_chain(Handler *handler, ...);

// 檢查handler通用函數
bool check_handler_start(Handler *handler, int param);


// 定義許可權檢查handler
typedef struct AuthHandler
{
    char name[50];
    Handler *next;
    bool (*check_handler)(struct Handler *, int);
} AuthHandler;

// 建立AuthHandler
AuthHandler *create_auth_handler(char *name);

// 定義請求檢查handler
typedef struct RequestHandler
{
    char name[50];
    Handler *next;
    bool (*check_handler)(struct Handler *, int);
} RequestHandler;

// 建立RequestHandler
RequestHandler *create_request_handler(char *name);

// 定義使用者檢查handler
typedef struct UserHandler
{
    char name[50];
    Handler *next;
    bool (*check_handler)(struct Handler *, int);
} UserHandler;

// 建立UserHandler
UserHandler *create_user_handler(char *name);

 

統一事件處理

// handler.c 基礎事件
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "func.h"

// 建立呼叫鏈,按順序形成鏈,返回第一個handler
Handler *link_handler(Handler *handler, Handler *next)
{
  handler->next = next;
  return handler;
}

// 不定引數建立呼叫鏈,第一個引數是handler的數量,後面是多個handler
Handler *make_handler_chain_count(int lenght, ...)
{
  va_list args;
  va_start(args, lenght);
  // 取出第1個handler
  Handler *first = va_arg(args, Handler *);
  Handler *head = first;
  // 把handler追加到next中,形成鏈,總長度減去第1個
  for (int i = 0; i < lenght - 1; i++)
  {
    head->next = va_arg(args, Handler *);
    head = head->next;
  }
  va_end(args);
  return first;
}

// 不定引數建立呼叫鏈,第一個引數是handler的數量,後面是多個handler,最後一個傳NULL
Handler *make_handler_chain(Handler *first, ...)
{
  va_list args;
  va_start(args, first);
  Handler *head = first;
  // 把handler追加到next中,以NULL作為結束符
  while (head != NULL)
  {
    head->next = va_arg(args, Handler *);
    head = head->next;
  }
  va_end(args);
  return first;
}

// 單獨handler檢查開始函數
bool check_handler_start(Handler *handler, int param)
{
  return handler->check_handler(handler, param);
}

 

不同事件,可以多個,互不關聯

```c
// auth_handler.c 許可權檢查類
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "func.h"

/* AuthHandler check函數實現 */
bool auth_handler_check(Handler *handler, int param)
{
    printf("\r\n auth_handler_check: [handler.name = %s param = %d]", handler->name, param);
    AuthHandler *auth_handler = (AuthHandler *)handler;
    // 這裡是判斷條件,如果出錯則終止呼叫鏈,返回false
    if (param % 2 == 0)
    {
        printf("\r\n auth_handler_check: error[ %d %s 2 ] == 0", param, "%");
        return false;
    }
    // 通過next呼叫下一步檢查
    if (handler->next != NULL)
    {
        return auth_handler->next->check_handler(handler->next, param);
    }
    return true;
}

/* 建立具體處理器的函數 */
AuthHandler *create_auth_handler(char *name)
{
    AuthHandler *handler = (AuthHandler *)malloc(sizeof(AuthHandler));
    strncpy(handler->name, name, 50);
    // 將handler的check_handler函數賦值為指定函數,便於檢查處理
    handler->check_handler = &auth_handler_check;
    handler->next = NULL;
    return handler;
}
```

```c
// request_handler.c 請求是否安全合法檢查
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "func.h"

/* RequestHandler check函數實現 */
bool request_handler_check(Handler *handler, int param)
{
  printf("\r\n request_handler_check: [handler.name = %s param = %d]", handler->name, param);
  RequestHandler *request_handler = (RequestHandler *)handler;
  // 這裡是判斷條件,如果出錯則終止呼叫鏈,返回false
  if (param % 5 == 0)
  {
    printf("\r\n request_handler_check: error[ %d %s 5 ] == 0", param, "%");
    return false;
  }
  // 通過next呼叫下一步檢查
  if (handler->next != NULL)
  {
    return request_handler->next->check_handler(handler->next, param);
  }
  return true;
}

/* 建立具體處理器的函數 */
RequestHandler *create_request_handler(char *name)
{
  RequestHandler *handler = (RequestHandler *)malloc(sizeof(RequestHandler));
  strncpy(handler->name, name, 50);
  // 將handler的check_handler函數賦值為指定函數,便於檢查處理
  handler->check_handler = &request_handler_check;
  handler->next = NULL;
  return handler;
}
```

```c
// user_handler.c 使用者基本資訊檢查類
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "func.h"

/* UserHandler check_handler函數實現 */
bool user_handler_check(Handler *handler, int param)
{
  printf("\r\n user_handler_check: [handler.name = %s param = %d]", handler->name, param);
  UserHandler *user_handler = (UserHandler *)handler;
  // 這裡是判斷條件,如果出錯則終止呼叫鏈,返回false
  if (param % 3 == 0)
  {
    printf("\r\n user_handler_check: error[ %d %s 3 ] == 0", param, "%");
    return false;
  }
  // 通過next呼叫下一步檢查
  if (handler->next != NULL)
  {
    return user_handler->next->check_handler(handler->next, param);
  }
  return true;
}

/* 建立具體處理器的函數 */
UserHandler *create_user_handler(char *name)
{
  UserHandler *handler = (UserHandler *)malloc(sizeof(UserHandler));
  strncpy(handler->name, name, 50);
  // 將handler的check_handler函數賦值為指定函數,便於檢查處理
  handler->check_handler = &user_handler_check;
  handler->next = NULL;
  return handler;
}
```

 

測試呼叫

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "../src/func.h"

int main(void)
{
    /**
     * 責任鏈模式核心是打造一個呼叫處理鏈,每個處理鏈都實現抽象類的next方法,從而可以任意組織各種檢查行為。
     * 通過改變鏈內的成員或者調動它們的順序,允許動態地新增或者刪除職責,從而實現按需組織。
     */

    // 建立一組hanler
    RequestHandler *request_handler = create_request_handler("request_handler_01");
    UserHandler *user_handler = create_user_handler("user_handler_02");
    AuthHandler *auth_handler = create_auth_handler("auth_handler_03");
    printf("建立handler:\r\n %s %s %s", request_handler->name, user_handler->name, auth_handler->name);

    // 將handler逐個連結成職責鏈
    link_handler((Handler *)request_handler, (Handler *)user_handler);
    link_handler((Handler *)user_handler, (Handler *)auth_handler);

    printf("\r\n建立職責鏈:\r\n");
    Handler *handler_cur = (Handler *)request_handler;
    while (handler_cur != NULL)
    {
        printf(" -> %s", handler_cur->name);
        handler_cur = handler_cur->next;
    }

    // 從任意handler開始檢查
    // printf("\r\ncheck_handler_start檢查:");
    // check_handler_start((Handler *)request_handler, 666);

    // 從執行handler開始
    printf("\r\n開始檢查:");
    bool result1 = request_handler->check_handler((Handler *)request_handler, 666);
    printf("\r\n執行結果: %s \r\n", result1 ? "true" : "false");

    /* 釋放記憶體 */
    free(handler_cur);
    free(request_handler);
    free(auth_handler);
    free(user_handler);

    /*** ========分割線============ ***/
    printf("\r\n=============\r\n");

    /* 建立一組hanler */
    RequestHandler *request_handler2 = create_request_handler("request_handler_101");
    UserHandler *user_handler2 = create_user_handler("user_handler_102");
    AuthHandler *auth_handler2 = create_auth_handler("auth_handler_103");
    printf("\r\n建立handler:\r\n %s %s %s", request_handler2->name, user_handler2->name, auth_handler2->name);

    // 將handler一次性連結為職責鏈,傳入多個handler,第一個引數是數量
    Handler *handler2 = make_handler_chain_count(3, auth_handler2, request_handler2, user_handler2);
    printf("\r\n建立職責鏈:\r\n");
    Handler *handler_cur2 = (Handler *)handler2;
    while (handler_cur2 != NULL)
    {
        printf(" -> %s", handler_cur2->name);
        handler_cur2 = handler_cur2->next;
    }

    // 呼叫通用檢查函數開始
    printf("\r\n開始檢查:");
    bool result2 = check_handler_start(handler2, 777);
    printf("\r\n執行結果: %s \r\n", result2 ? "true" : "false");

    /* 釋放記憶體 */
    free(handler_cur2);
    free(request_handler2);
    free(auth_handler2);
    free(user_handler2);

    /*** ========分割線============ ***/
    printf("\r\n=============\r\n");
    /* 再建立一組hanler */
    RequestHandler *request_handler3 = create_request_handler("request_handler_201");
    UserHandler *user_handler3 = create_user_handler("user_handler_202");
    AuthHandler *auth_handler3 = create_auth_handler("auth_handler_203");
    printf("\r\n建立handler:\r\n %s %s %s", request_handler3->name, user_handler3->name, auth_handler3->name);
    // 將handler一次性連結為職責鏈,傳入多個handler,最後一個引數是NULL
    Handler *handler3 = make_handler_chain((Handler *)auth_handler3, user_handler3, request_handler3, NULL);
    Handler *handler_cur3 = (Handler *)handler3;
    printf("\r\n建立職責鏈:\r\n");
    while (handler_cur3 != NULL)
    {
        printf(" -> %s", handler_cur3->name);
        handler_cur3 = handler_cur3->next;
    }
    printf("\r\n開始檢查:");
    bool result3 = check_handler_start(handler3, 167);
    printf("\r\n執行結果: %s \r\n", result3 ? "true" : "false");

    /* 釋放記憶體 */
    free(handler_cur3);
    free(request_handler3);
    free(auth_handler3);
    free(user_handler3);

    return 0;
}

 

更多語言版本

不同語言實現設計模式:https://github.com/microwind/design-pattern