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

Blog#52: Design Patterns: Singleton Pattern và Anti-Pattern trong TypeScript 😊 (Series: Bón hành TypeScript - PHẦN 2)

0 0 18

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

Theo Viblo Asia

Mình là TUẤN hiện đang là một Full-stack 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 Singleton Pattern bằng TypeScript để giải quyết các vấn đề thực tế trong các project web giúp Chia sẻ một Global Instance duy nhất trong toàn bộ ứng dụng.

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 Singleton Pattern .

Singleton Pattern

Singleton Pattern là một mẫu phổ biến và chúng ta thường chỉ cần một trong một số đối tượng, chẳng hạn như bộ đệm chung, đối tượng windown trong Browsers, v.v. Singleton Pattern được sử dụng để đảm bảo rằng chỉ có một Instance của một Class và để cung cấp một điểm truy cập global duy nhất cho nó .

Chúng ta hãy xem cách sử dụng TypeScript để implement Singleton Pattern.

class Singleton { private static singleton: Singleton; // ① private constructor() {} // ② public static getInstance(): Singleton { // ③ if (!Singleton.singleton) { Singleton.singleton = new Singleton(); } return Singleton.singleton; }
}

Khi định nghĩa class Singleton, có ba bước quan trọng:

  1. Xác định một private static Properties để giữ đối tượng Instance ;
  2. Xác định một private constructor;
  3. Cung cấp một public static getInstance() để lấy một Instance đối tượng.

Trong VSCode, đối với các private static Properties, IntelliSense của VSCode sẽ tự động lọc ra các Properties này:

Đối với private constructor, khi chúng ta tạo một Instance thông qua new Singleton(), trình biên dịch TypeScript sẽ nhắc thông báo lỗi sau:

Constructor of class 'Singleton' is private and only accessible within the class declaration.ts(2673)

Với class Singleton, hãy thử check xem các instance của nó có thật là duy nhất:

let instance1 = Singleton.getInstance();
let instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true

Cách dễ nhất để implement một singleton trong JavaScript là sử dụng một đối tượng theo đúng nghĩa đen

const httpClient = { get(url, config) { // send get request }, post(url, config) { // send post request },
};

Nếu bạn cần chứa thêm các private properties hoặc methods, bạn có thể sử dụng các cách sau để tạo một singleton:

const httpClient = (function () { // private method function sendRequest(url, config) { // send request } return { get(url, config) { return sendRequest(url, config); }, post(url, config) { return sendRequest(url, config); }, };
})();

Nếu bạn đang sử dụng ES6, bạn có thể khai báo một singleton bằng ES Modules rất dễ dàng:

export default { get(url, config) { // send get request }, post(url, config) { // send post request },
};

Axios được sử dụng rộng rãi trong các project web khác nhau và đối tượng axios được cung cấp cũng là một đối tượng Singleton.

import axios from "axios";
axios.get('/users') .then(res => { console.log(res.data); });

Ví dụ trên axios sử dụng đối tượng configure mặc định, tất nhiên Axios cũng cho phép chúng ta sử dụng hàm axios.create để tạo một instance mới của axios với configure tùy chỉnh.

const instance = axios.create({ baseURL: 'https://mediuem.com/', timeout: 1000, headers: {'X-Custom-Header': 'Bytefer'}
});

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

  • Các đối tượng mất quá nhiều thời gian hoặc tài nguyên để tạo nhưng lại thường được sử dụng lại nhiều lần.
  • Vì Singleton Pattern chỉ có một Instance trong bộ nhớ nên mức sử dụng bộ nhớ giảm, đặc biệt là khi một đối tượng cần được tạo và hủy thường xuyên => không thể tối ưu hóa hiệu suất trong quá trình tạo hoặc hủy, đó là luc Singleton Pattern phát huy hết sức mạnh của mình.

Tuy nhiên không phải lúc nào Singleton Pattern cũng hữu dụng đôi khi sử dụng nó cũng sẽ là một Anti-Pattern.

Ví dụ: Chắc bạn nào code Nodejs + Postgres thì ko lạ gì cái thư viện pg này rồi đúng ko? Việc Connect tốn time và cũng tốn tài nghuyên. Woa chuẩn rồi công thức rồi áp dụng công thức thôi.

import { Pool } from 'pg'; const pool = new Pool(); export async function query(query: any) { const { rows } = await pool.query(query); return rows;
}

pool giờ là một Singleton Instance ok dùng thôi.

import { query } from '../' async function doSomething() { try { await query('BEGIN') const queryText = 'INSERT INTO users(name) VALUES($1) RETURNING id' const res = await query(queryText, ['brianc']) const insertPhotoText = 'INSERT INTO photos(user_id, photo_url) VALUES ($1, $2)' const insertPhotoValues = [res.rows[0].id, 's3.bucket.foo'] await query(insertPhotoText, insertPhotoValues) await query('COMMIT') } catch (e) { await query('ROLLBACK') throw e }
}
await doSomething()

Opps!!! ko ổn chút nào trong trường hợp này thì nếu dùng 1 Instance thì lại phản tác dụng -> bug

Theo node-postgres doc: Để thực hiện một transaction với node-postgres, bạn chỉ cần thực hiện các truy vấn BEGIN/COMMIT/ROLLBACK thông qua một Instance là OK. Tuy nhiên lưu ý: Bạn phải sử dụng cùng một Instance client cho tất cả các câu lệnh trong một transaction. PostgreSQL cô lập một transaction cho các client. Điều này có nghĩa là nếu bạn khởi tạo hoặc sử dụng các transaction bằng phương thức pool.query, bạn sẽ gặp **LỖI**. Khuyến cáp không sử dụng các transaction với phương thức pool.query.

Cách đúng sẽ là:

const { Pool } = require('pg')
const pool = new Pool() // note: we don't try/catch this because if connecting throws an exception
// we don't need to dispose of the client (it will be undefined)
const client = await pool.connect() try { await client.query('BEGIN') const queryText = 'INSERT INTO users(name) VALUES($1) RETURNING id' const res = await client.query(queryText, ['brianc']) const insertPhotoText = 'INSERT INTO photos(user_id, photo_url) VALUES ($1, $2)' const insertPhotoValues = [res.rows[0].id, 's3.bucket.foo'] await client.query(insertPhotoText, insertPhotoValues) await client.query('COMMIT')
} catch (e) { await client.query('ROLLBACK') throw e
} finally { client.release()
}

=> Khi bạn sử dụng một Design Pattern nào đó thậm chí công thức y hệt như SGK thì cũng có lúc tạch là chuyện rất bình thường. Thầy cô VN cũng có câu kinh điển: Môn TOÁN giễ lắm mấy em chỉ cần áp dụng công thức là ra. Đúng thật chỉ cần áp dụng công thức là ra.... mà là ra khỏi phòng thi nộp bái sớm.

Thời gian tới mình cũng sẽ viết một bài về Anti-Pattern và sẽ đào sâu về vấn đề này. Các bạn nhớ theo dõi mình để đón đọc nhé.

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 499

- 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 32

- 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 25

- 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 35

- 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 58

- 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 82