- vừa được xem lúc

Blog#56: Design Patterns: Command Pattern trong TypeScript 😊 (Series: Bón hành TypeScript - PHẦN 6)

0 0 22

Người đăng: NGUYỄN ANH TUẤN

Theo Viblo Asia

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é 😉.

Chào mừng bạn đến với loạt bài Design Patterns trong TypeScript, tại đây mình 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 Command Pattern.

Command Pattern tách rời Caller và Receiver. Cho phép bạn dễ dàng thêm các lệnh khác nhau để thêm các chức năng khác nhau.

Kịch bản sử dụng Pattern

Trên iOS và macOS, đã có chức năng Shortcuts được tích hợp sẵn. Với chức năng này, các bạn có thể thực hiện nhanh chóng thực hiện 1 hoặc nhiều tác vụ. Ví dụ: các tác vụ như gửi tin nhắn nhanh, dịch văn bản, rút ​​ngắn URL và Download tệp.

Tiếp theo, hãy thực hiện một chức năng tương tự. Trong đoạn code sau, chúng ta định nghĩa một lớp Shortcuts, trong đó chúng ta tạo 5 hàm thành viên như openUrl, shortenUrl, sendMessage, v.v.

class Shortcuts { openUrl(url: string) { console.log(`Open url: ${url}`); } shortenUrl(url: string) { console.log(`Shorten url: ${url}`); } sendMessage(msg: string) { console.log(`Send message: ${msg}`); } translateText(originText: string) { console.log(`Translate text: ${originText}`); } downloadFile(fileUrl: string) { console.log(`Download file: ${fileUrl}`); }
}

Sau đó chúng ta định nghĩa một class UIEventHandler chứa hàm handleAction để xử lý các sự kiện của user.

class UIEventHandler { constructor(public shortcuts: Shortcuts) {} handleAction(action: ShortcutsMethods, arg: string) { this.shortcuts[action](arg); }
} // "openUrl" | "shortenUrl" | "sendMessage" | "translateText" | "downloadFile"
type ShortcutsMethods = Methods<Shortcuts>; type Methods<T> = { [P in keyof T]: T[P] extends (...args: any) => void ? P : never;
}[keyof T];

Phương thức handleAction nhận 2 tham số là actionarg. Type của tham số actionShortcuts, được tạo thông qua utility type Methods. Nếu bạn muốn tìm hiểu thêm về các Mapped types, mình khuyên bạn nên tìm đọc một bài viết của mình về chủ đề này.

Với class UIEventHandler, chúng ta có thể sử dụng nó theo cách sau:

const shortcuts = new Shortcuts();
const eventHandler = new UIEventHandler(shortcuts); eventHandler.handleAction("openUrl", "https://medium.com/@bytefer");
eventHandler.handleAction("sendMessage", "Hello Bytefer!");

Đối với code trước đó, có vẻ như không có vấn đề gì. Nhưng sau khi phân tích cẩn thận, bạn sẽ thấy các vấn đề sau:

  • Khi gọi hàm handleAction, chúng ta cần đảm bảo rằng tên action đó phải phù hợp với tên của hàm trong lớp Shortcuts.
  • Với sự gia tăng liên tục của các chức năng, sẽ có ngày càng nhiều hàm tương ứng trong lớp Shortcuts. Kết quả là, chúng ta cần liên tục sửa đổi lớp Shortcuts.

Vậy chúng ta nên giải quyết vấn đề trên như thế nào? Đối với vấn đề này, chúng ta có thể sử dụng Command Pattern.

Command Pattern

Trước tiên chúng ta hãy xem sơ đồ UML tương ứng:

Trên thực tế, chúng ta có thể gói các tác vụ như gửi tin nhắn, dịch văn bản và rút ngắn URL thành các lệnh riêng lẻ.

interface Command { name: string; execute(args: any): any;
}

Trong đoạn code trên, chúng ta sử dụng các keyword interface để xác định type Command. Trong kiểu Command, một hàm execute được định nghĩa để đóng gói logic mà mỗi lệnh cần thực hiện. Với Interface Command, hãy xác định các lệnh cụ thể.

class OpenUrlCommand implements Command { name = "openUrl"; execute(args: any) { console.log(`Open url: ${args[0]}`); }
} class SendMessageCommand implements Command { name = "sendMessage"; execute(args: any) { console.log(`Send message: ${args[0]}`); }
}

Trong đoạn code trên, chúng ta đã tạo các lớp OpenUrlCommandSendMessageCommand. Trong tương lai, các lệnh của chúng ta sẽ tiếp tục tăng lên. Để thuận tiện cho việc quản lý các lớp lệnh khác nhau, chúng ta cần định nghĩa một lớp để quản lý các lệnh:

class CommandManager { commands: Record<string, Command> = {}; registerCommand(name: string, command: Command) { this.commands[name] = command; } executeCommand(command: string | Command, ...args: any) { if (typeof command === "string") { this.commands[command].execute(args); } else { command.execute(args); } }
}

Trong lớp CommandManager, hàm registerCommand được sử dụng để đăng ký lệnh. Và hàm executeCommand được sử dụng để thực thi một lệnh. Với lớp CommandManager, hãy cập nhật lớp UIEventHandler đã tạo trước đó.

class UIEventHandler { constructor(public cmdManager: CommandManager) {} handleAction(command: string | Command, arg: string) { this.cmdManager.executeCommand(command, arg); }
}

Sau khi cập nhật lớp UIEventHandler, hãy khai báo chức năng tương ứng của nó.

const commandManager = new CommandManager();
commandManager.registerCommand("openUrl", new OpenUrlCommand());
commandManager.registerCommand("msg", new SendMessageCommand()); const eventHandler = new UIEventHandler(commandManager);
eventHandler.handleAction("openUrl", "https://medium.com/@bytefer");
eventHandler.handleAction("msg", "Hello Medium!");
eventHandler.handleAction(new SendMessageCommand(), "Hello Bytefer!");
);

Trong đoạn code trên, trước tiên chúng ta tạo đối tượng CommandManager và đăng ký 2 lệnh. Sau đó, chúng ta tạo một đối tượng UIEventHandler và sử dụng hàm handleAction trên đối tượng để thực thi lệnh đã đăng ký. Sau khi đoạn code trên được thực thi thành công, Interface điều khiển sẽ xuất thông tin sau:

Open url: https://medium.com/@bytefer
Send message: Hello Medium!
Send message: Hello Bytefer!

Trong contexts của text editor và chức năng command-line, Command Pattern cũng thường được sử dụng. Ví dụ: CAC, thư viện của bên thứ ba để tạo chức năng dòng lệnh, cũng sử dụng Command Pattern. Nếu bạn quan tâm, bạn có thể đọc source code tương ứng.

Các kịch bản sử dụng của Command Pattern:

  • Khi cần trừu tượng hóa các hành động thực thi khác nhau, các tham số khác nhau được sử dụng để xác định hành động nào sẽ thực hiện.
  • Hệ thống cần tách rời Caller request và Receiver request để Caller và Receiver không tương tác trực tiếp. Caller request không cần biết sự tồn tại của Receiver, cũng không cần biết Receiver là ai và Receiver không cần quan tâm khi nào nó được gọ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.😉

Ref

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 496

- vừa được xem lúc

Type annotation vs Type Inference - Typescript

Trong bài viết này, chúng ta sẽ tìm hiểu kỹ về TypeScript bằng cách tìm hiểu sự khác biệt giữa kiểu chú thích và kiểu suy luận. Tôi sẽ cho rằng bạn có một số kinh nghiệm về JavaScript và biết về các kiểu cơ bản, như chuỗi, số và boolean.

0 0 31

- vừa được xem lúc

Type Annotation và Type Inference trong TypeScript là gì?

Khi làm việc với javascript chắc hẳn các bạn đã quá quen với việc dùng biến mà không cần phải quan tâm đến kiểu dữ liệu của nó là gì phải không? Đúng là mới đầu tiếp cận với Typescript mình cũng cảm thấy nó khá là phiền vì cần phải khai báo đủ type để nó chặt chẽ hơn. Lúc đó mình còn nghĩ: " JavaScr

0 0 24

- vừa được xem lúc

Tìm hiểu TypeScript và kiến thức cơ bản

TypeScript là gì. TypeScript sử dụng tất cả các tính năng của của ECMAScript 2015 (ES6) như classes, modules.

0 0 33

- vừa được xem lúc

TypeScript - P1: Vì sao TypeScript được yêu thích đến vậy?

Trải nghiệm thực tế. Trước khi là một Web Developer, tôi là một Mobile Developer và Java là thứ mà tôi từng theo đuổi.

0 1 57

- vừa được xem lúc

4 Tính năng rất hay từ TypeScript

Xin chào các bạn hôm nay mình xin chia sẽ những tính năng rất hay của TypeScript (TS), các bạn cùng tìm hiểu nhé. Ngoài việc set Type cho biến, tham số hay function thì ví dụ khi bạn nhìn vào một tham

0 0 81