+8

Design Patterns: Template Method Pattern trong TypeScript 😊 (Series: Bón hành TypeScript - PHẦN 7)

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 Template Method Pattern.

Kịch bản thưởng gặp

CSV (Comma-Separated Values - Các value được phân tách bằng dấu phẩy) là định dạng tệp tương đối đơn giản. Tệp CSV lưu trữ dữ liệu dạng bảng (numbers and text) ở dạng văn bản thuần túy. Khi bạn cần xử lý dữ liệu CSV, quy trình xử lý tương ứng được hiển thị trong hình sau:

image.png

Sau khi hiểu được quy trình xử lý trên, chúng ta hãy sử dụng Node.js để implement function Parsing tệp csv.

users.csv

id,Name
1,Bytefer
2,Kakuqo

parse-csv.ts

import fs from "fs";
import path from "path";
import * as url from "url";
import { csvParse } from "d3-dsv";
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
const processData = (fileData: any[]) => console.dir(fileData);
const content = fs.readFileSync(path.join(__dirname, "users.csv"), "utf8");
const fileData = csvParse(content);
processData(fileData);

Trong đoạn code trên, chúng ta import mô-đun d3-dsv để implement function Parsing csv. Sau đó, chúng ta sử dụng esno để thực thi tệp parse-cvs.ts :

$ npx esno parse-csv.ts

Khi đoạn code trên chạy thành công, terminal sẽ xuất ra kết quả như sau:

[
  { id: '1', Name: 'Bytefer' },
  { id: '2', Name: 'Kakuqo' },
  columns: [ 'id', 'Name' ]
]

Tiếp theo, Markdown là một ngôn ngữ đánh dấu văn bản nhẹ cho phép mọi người viết tài liệu ở định dạng văn bản thuần túy, dễ đọc và dễ viết. Để hiển thị tài liệu Markdown trên các trang web, chúng ta phải chuyển đổi tài liệu Markdown thành tài liệu HTML.

Để thực hiện được function trên, quy trình xử lý của chúng ta như sau:

image.png

Sau khi hiểu được quy trình xử lý trên, chúng ta hãy sử dụng Node.js để implement function Parsing Markdown.

Users.md

### Users
- Bytefer
- Kakuqo

parse-md.ts

import fs from "fs";
import path from "path";
import * as url from "url";
import { marked } from 'marked';
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
const processData = (fileData: any[]) => console.dir(fileData);
const content = fs.readFileSync(path.join(__dirname, "Users.md"), "utf8");
const fileData = marked.parse(content);
processData(fileData);

Trong đoạn code trên, chúng ta import mô-đun marked để thực hiện function Parsing Markdown. Sau đó, chúng ta lại sử dụng esno để thực thi tệp parse-md.ts :

$ npx esno parse-md.ts

Khi đoạn code trên chạy thành công, terminal sẽ xuất ra kết quả như sau:

'<h3 id="users">Users</h3>\n<ul>\n<li>Bytefer</li>\n<li>Kakuqo</li>\n</ul>\n'

Đối với hai ví dụ trước, mặc dù các loại tệp khác nhau được Parsing, nhưng bạn sẽ thấy rằng quá trình Parsing của chúng là tương tự nhau.

image.png

Toàn bộ quá trình chủ yếu bao gồm ba bước: đọc, Parsing và xử lý dữ liệu. Đối với kịch bản này, chúng ta có thể apply Template Method Pattern để gói gọn trình tự xử lý của ba bước trên.

Template Method Pattern

Template Method Pattern bao gồm hai phần: một abstract parent class và một implementation subclass cụ thể. Thông thường, khung thuật toán của subclass được đóng gói trong abstract parent class và nó cũng bao gồm việc thực hiện một số hàm public và thứ tự thực hiện của tất cả các hàm trong subclass được đóng gói. Bằng cách kế thừa abstract class này, các subclass cũng kế thừa toàn bộ cấu trúc thuật toán và có thể chọn ghi đè các hàm của parent class.

Tiếp theo, hãy xem cách implement trình Parsing CSV và Parsing Markdown bằng cách sử dụng Template Method Pattern.

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

image.png

Trong hình trên, chúng ta định nghĩa một abstract class FileParser và sau đó định nghĩa hai subclass, CsvParserMarkdownParser, tương ứng.

FileParser class

abstract class FileParser {
  // Template Method
  parse(filePath: string) {
    let content = this.readFile(filePath);
    let fileData = this.parseFile(content);
    this.processData(fileData);
  }
  readFile(filePath: string) {
    if (fs.existsSync(filePath)) {
      return fs.readFileSync(filePath, "utf8");
    }
  }
  abstract parseFile(fileContent: string): any;
  processData(fileData: any[]) {
    console.log(fileData);
  }
}

Phương thức parse trong abstract class FileParser được gọi là Template method, trong đó chúng ta đóng gói quá trình xử lý tệp.

CsvParser class

class CsvParser extends FileParser { parseFile(fileContent: string) { return csvParse(fileContent); } }

MarkdownParser class

class MarkdownParser extends FileParser { parseFile(fileContent: string) { return marked.parse(fileContent); } }

Với hai lớp CsvParserMarkdownParser, chúng ta có thể Parsing CSV và Markdown theo các cách sau:

const csvParser = new CsvParser();
csvParser.parse(path.join(__dirname, "Users.csv"));

const mdParser = new MarkdownParser();
mdParser.parse(path.join(__dirname, "Users.md"));

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

Bằng cách sử dụng Template Method Pattern, chúng ta đã implement lại việc Parsing CSV và Markdown. Trên thực tế, với abstract class FileParser, chúng ta có thể dễ dàng phát triển các chức năng Parsing cú pháp tệp khác nhau mà ko cần sửa Abstract class.

Các tình huống sử dụng của Template Method Pattern:

  • Các bước tổng thể của thuật toán là rất cố định, nhưng khi các phần riêng lẻ có thể thay đổi, Template Method Pattern có thể được sử dụng tại thời điểm này để trừu tượng hóa các phần dễ thay đổi cho các subclass thực hiện.

Các vấn đề tương tư như trên cũng hoàn toàn giải quyết được bằng Dependency Injection Pattern. Tương lai mình cũng sẽ có bài viết về chủ đề này các bạn nhớ đón xem nhé.

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í