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

image.png

Mình là TUẤN hiện đang là một Full-stack Web Developer tại Tokyo 😊. Nếu bạn thấy Blog này hay xin hãy cho mình một like và đăng ký để ủng hộ mình nhé 😉.

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 sender và receiver 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 AuthMiddleware và LoggerMidddleware 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 AuthMiddleware và LoggerMidddleware, 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.

Roundup

Như mọi khi, 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.

Cảm ơn và hẹn gặp lại các bạn trong những bài viết tiếp theo! 😍

Nếu bạn thấy Blog này hay xin hãy cho mình một like và đăng ký để ủng hộ mình nhé. Thank you.😉

Nhận xét

Bài đăng phổ biến từ blog này

Blog#1: Những kiến thức JavaScript căn bản mà một React Dev phải thuộc nằm lòng. - (Series: Bí kíp Javascript - PHẦN 1)

Blog#2: Bí kíp về mảng trong JavaScript - Các các hàm thường sử dụng với Array kèm ví dụ trực quan - (Series: Bí kíp Javascript - PHẦN 2)

Blog#3: Một số mô hình lập trình phổ biến (kèm ví dụ) cho người mới bắt đầu - Programming Paradigms - (Series: Bí kíp Javascript - PHẦN 3)