iT邦幫忙

2024 iThome 鐵人賽

DAY 16
0
Software Development

前端也想學設計模式系列 第 16

Day 16 - Chain of Responsibility 責任鏈

  • 分享至 

  • xImage
  •  

將請求的處理程序分成多個處理器,並讓每個處理器決定如何處理請求。

生活案例

想像蘋果工廠的流水線上有一系列工作站,分別負責清洗、選別和包裝。每個工作站會依序處理蘋果,並將其傳送至下一個站。如果在選別站發現蘋果有嚴重破損或蟲害,蘋果會被挑出並提前終止處理程序。這些工作站就像責任鏈模式中的處理器,每個站都有特定的職責,可以選擇處理或將蘋果傳遞至下一個站,也可以提前終止流程。

舉個例子

大家對於前後端應用程式中的路由應該不陌生。路由器會依照順序比對每個路徑,將請求交給對應的處理器,這與責任鏈模式的設計概念非常相似。我們來實作一個簡易版的音樂伺服器路由,看看如何運用責任鏈模式來開發程式。

首先,定義一個抽象的路由處理器。每個路由器具體的處理器都要實現自己的處理邏輯。我們利用 setSuccessor 方法可以設定下個處理器,並透過 next 方法將請求傳遞至下個處理器。

abstract class RequestHandler {
  private successor?: RequestHandler;

  abstract handle(request: Request): void;

  setSuccessor(successor: RequestHandler) {
    this.successor = successor;
  }

  protected next(request: Request) {
    this.successor?.handle(request);
  }
}

實作具體處理器。日誌程式用來紀錄每個請求路徑,專輯程式則會回應一個專輯列表給使用者。

class Logger extends RequestHandler {
  handle(request: Request) {
    console.log(`Logger: Received request for ${request.pathname}`);
    this.next(request);
  }
}

class AlbumsHandler extends RequestHandler {
  handle(request: Request) {
    if (request.pathname !== "/albums") {
      return this.next(request);
    }
    console.log("AlbumsHandler: Sending several albums to user");
  }
}

設計伺服器程式。每當伺服器收到一個新的請求時,會將請求交給第一個處理器,並讓處理器決定要處理請求,或將請求傳遞至下一個處理器。

class Server {
  private handlers: RequestHandler[];

  constructor() {
    this.handlers = [];
  }

  use(handler: RequestHandler) {
    const previousHandler = this.handlers[this.handlers.length - 1];
    previousHandler?.setSuccessor(handler);
    this.handlers.push(handler);
  }

  process(request: Request) {
    if (this.handlers.length > 0) {
      this.handlers[0].handle(request);
    }
  }

  listen() {
    console.log("Listening for incoming requests...");
  }
}

測試伺服器。

class ServerTestDrive {
  static main() {
    const server = new Server();
    const logger = new Logger();
    const albumsHandler = new AlbumsHandler();
    const artistsHandler = new ArtistsHandler();

    server.use(logger);
    server.use(albumsHandler);
    server.use(artistsHandler);

    server.listen();

    server.process(new Request("/albums"));
    server.process(new Request("/artists"));
    server.process(new Request("/unknown"));
  }
}

ServerTestDrive.main();

輸出結果。

Listening for incoming requests...
Logger: Received request for /albums
AlbumsHandler: Sending several albums to user
Logger: Received request for /artists
ArtistsHandler: Sending several artists to user
Logger: Received request for /unknown

定義

Chain of Responsibility Pattern

  • 抽象處理器(Handler): 定義通用的執行方法,並保存下個處理器的參照
  • 具體處理器(ConcreteHandler): 提供執行方法的具體實現,處理或傳遞請求至下個處理器

責任鏈模式將請求的處理程序分成多個處理器,讓每個處理器專注於特定的任務,藉此分散職責。這樣的設計可以降低程式間的依賴性,讓系統更容易維護與擴充。責任鏈會將請求依序傳遞至各個處理器,直到請求被處理,或是所有處理器都執行完畢。每個處理器可以自行選擇要處理或忽略請求,也能視情況提前終止責任鏈。

總結

  • 將請求的處理程序分成多個處理器,並讓每個處理器各司其職
  • 請求會被依序傳遞至各個處理器,直到請求被處理,或是所有處理器都執行完畢
  • 處理器可以自行選擇要處理或忽略請求,並在需要時提前終止責任鏈

完整範例

https://github.com/chengen0612/design-patterns-typescript/blob/main/patterns/behavioral/chain-of-responsibility.ts


上一篇
Day 15 - Builder 建造者
下一篇
Day 17 - Template Method 樣板方法
系列文
前端也想學設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言