+7

Design Patterns: Chain of Responsibility Pattern trong TypeScript 😊 (Series: Bón hành TypeScript - PHẦN 4)

Cách sử dụng Chain of Responsibility bằng TypeScript để giải quyết các vấn đề thực tế trong các project web.

Chào mừng bạn đến với loạt bài Design Patterns trong TypeScript, loạt bài này mình sẽ giới thiệu một số Design Patterns hữu ích trong phát triển web bằng TypeScript.

Các Design Patterns rất quan trọng đối với các web developer và chúng ta có thể code tốt hơn bằng cách thành thạo chúng. Trong bài viết này, mình sẽ sử dụng TypeScript để giới thiệu Chain of Responsibility.

Các Design Patterns rất quan trọng đối với các Dev web và chúng ta có thể good code hơn bằng cách thành thạo chúng. Trong bài viết này, mình sẽ sử dụng TypeScript để giới thiệu Chain of Responsibility .

Chain of Responsibility

Chain of Responsibility là một cách để tránh ghép nối giữa senderreceiver của các request bằng cách cho nhiều đối tượng xử lý request. Trong Chain of Responsibility, nhiều đối tượng được kết nối bằng một tham chiếu từ mỗi đối tượng đến đối tượng tiếp theo của nó để tạo thành một chuỗi (next,next,next...). Các request được truyền dọc theo chuỗi cho đến khi một trong các đối tượng trong chuỗi quyết định xử lý request.

image.png

Các vị trí khác nhau trong công ty có trách nhiệm và quyền hạn khác nhau. Lấy ví dụ về quy trình nghỉ của một công ty, khi xin nghỉ chỉ cần được sự đồng ý của tổ trưởng, không cần phải chuyển cho cấp trên và giám đốc. Nếu một liên kết trong Chain of Responsibility không thể xử lý request hiện tại, nếu có liên kết tiếp theo, request sẽ được chuyển tiếp đến liên kết tiếp theo để xử lý.

Trong quá trình phát triển phần mềm, đối với Chain of Responsibility, một kịch bản ứng dụng phổ biến là middleware. Chúng ta hãy xem cách sử dụng Chain of Responsibility để xử lý các request.

Để hiểu rõ hơn về đoạn code sau, trước tiên chúng ta hãy xem sơ đồ UML tương ứng:

image.png

Trong hình trên, chúng ta xác định một Interface Handler. Hai hàm sau đây được định nghĩa trong Interface này:

  • use(h: Handler): Handler => Dùng để đăng ký handler (middleware)
  • get(url: string, callback: (data: any) => void): void => Đăng ký get request handler

Handler interface

interface Handler {
  use(h: Handler): Handler;
  get(url: string, callback: (data: any) => void): void;
}

Sau đó, chúng ta định nghĩa một abstract Class AbstractHandler, gói gọn logic xử lý của Chain of Responsibility. Tức là kết hợp các trình xử lý khác nhau để tạo thành một chuỗi tham chiếu.

AbstractHandler abstract class

abstract class AbstractHandler implements Handler {
  next!: Handler;
  use(h: Handler) {
    this.next = h;
    return this.next;
  }
  get(url: string, callback: (data: any) => void) {
    if (this.next) {
      return this.next.get(url, callback);
    }
  }
}

Dựa trên abstract Class AbstractHandler, chúng ta định nghĩa AuthMiddlewareLoggerMidddleware tương ứng. AuthMiddleware middleware được sử dụng để xử lý authentication user và LoggerMidddleware middleware được sử dụng để ghi log cho từng request.

AuthMiddleware class

class AuthMiddleware extends AbstractHandler {
  isAuthenticated: boolean;
  constructor(username: string, password: string) {
    super();
    this.isAuthenticated = false;
    if (username === "bytefer" && password === "666") {
      this.isAuthenticated = true;
    }
  }
  get(url: string, callback: (data: any) => void) {
    if (this.isAuthenticated) {
      return super.get(url, callback);
    } else {
      throw new Error("Not Authorized");
    }
  }
}

LoggerMiddleware class

class LoggerMiddleware extends AbstractHandler {
  get(url: string, callback: (data: any) => void) {
    console.log(`Request url is: ${url}`);
    return super.get(url, callback);
  }
}

Với middleware AuthMiddlewareLoggerMidddleware, hãy định nghĩa một Route class để đăng ký các middleware này.

Route class

class Route extends AbstractHandler {
  urlDataMap: { [key: string]: any };
  constructor() {
    super();
    this.urlDataMap = {
      "/api/todos": [
        { title: "Learn Design Pattern" },
      ],
      "/api/random": () => Math.random(),
    };
  }
 get(url: string, callback: (data: any) => void) {
    super.get(url, callback);
  if (this.urlDataMap.hasOwnProperty(url)) {
      const value = this.urlDataMap[url];
      const result = typeof value === "function" ? value() : value;
      callback(result);
    }
  }
}

Sau khi định nghĩa Route Route class, chúng ta có thể sử dụng nó và đăng ký các middleware theo cách sau:

const route = new Route();
route.use(new AuthMiddleware("bytefer", "666"))
 .use(new LoggerMiddleware());
route.get("/api/todos", (data) => {
  console.log(JSON.stringify({ data }, null, 2));
});
route.get("/api/random", (data) => {
  console.log(data);
});

image.png

Khi bạn chạy thành công đoạn code trên, output tương ứng được hiển thị trong hình sau:

image.png

Các tình huống sử dụng của Chain of Responsibility:

  • Muốn gửi request tới một trong nhiều đối tượng mà không chỉ định rõ ràng đối tượng nhận request.
  • Có nhiều đối tượng có thể xử lý một request và đối tượng nào xử lý request được xác định tự động trong thời gian chạy và Client chỉ cần gửi request đến Chain mà thôi.

Mình hy vọng bạn thích bài viết này và học thêm được điều gì đó mới.

Donate mình một ly cafe hoặc 1 cây bút bi để mình có thêm động lực cho ra nhiều bài viết hay và chất lượng hơn trong tương lai nhé. À mà nếu bạn có bất kỳ câu hỏi nào thì đừng ngại comment hoặc liên hệ mình qua: Zalo - 0374226770 hoặc Facebook. Mình xin cảm ơn.

Momo: NGUYỄN ANH TUẤN - 0374226770

TPBank: NGUYỄN ANH TUẤN - 0374226770 (hoặc 01681423001)

image.png


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí