Singleton là 1 trong 5 design pattern của nhóm khởi tạo (Creational Design Pattern).
Định nghĩa:
Singleton is a creational design pattern that lets you ensure that a class has only one instance, while providing a global access point to this instance.
Dịch: Singleton là 1 mẫu design pattern thuộc nhóm khởi tạo cho phép bạn đảm bảo rằng 1 lớp sẽ chỉ có duy nhất 1 instance và nó cung cấp 1 method cho instance này ở bất cứ đâu trong chương trình.
Singleton giải quyết bài toán nào?
Singleton Pattern giải quyết 2 vấn đề dưới đây cùng 1 lúc:
- Đảm bảo rằng 1 lớp (class) sẽ chỉ có 1 instance duy nhất: Sẽ có 1 số trường hợp mà bạn cần kiểm soát việc truy cập đến các tài nguyên dùng chung ví dụ như database hay 1 file nào đó; lúc này bạn cần kiểm soát được số lượng instance mà 1 class đó có.
Cách nó hoạt động sẽ như sau: Thử tưởng tượng rằng bạn đã tạo 1 object rồi, tuy nhiên sau đó bạn lại quyết định tạo thêm 1 object mới. Lúc này thay vì việc nhận được 1 object mới thì bạn sẽ nhận về object mà bạn tạo ra lúc trước.
Lưu ý rằng hành vi này không thể thực hiện với 1 phương thức khởi tạo thông thường (như sử dụng new), vì nó sẽ luôn trả về 1 object mới.
- Cung cấp 1 điểm truy cập global đến instance đó: Biến global (toàn cục) thường được sử dụng để lưu trữ 1 số đối tượng thiết yếu. Mặc dù nó rất là tiện dụng, nhưng chúng cũng rất không an toàn vì bất cứ đoạn code nào trong chương trình cũng có thể ghi đè nội dung của những biến đó khiến cho ứng dụng của chúng ta bị crash. Singleton cũng giống như biến toàn cục, nó cho phép bạn truy cập đến 1 số object ở bất kỳ đâu trong chương trình, tuy nhiên nó cũng bảo vệ instance đó tránh khỏi việc bị ghi đè bởi code khác.
Có 1 cách nhìn khác cho vấn đề này: bạn không muốn phần code giải quyết vấn đề #1 ở trên bị phân tán khắp nơi trong chương trình, source code của mình. Sẽ tốt hơn khi chúng được viết hết trong 1 class đặc biệt là nếu các phần code khác đã phụ thuộc vào nó.
Cách triển khai Singleton
Tất cả các triển khai (implementations) của Singleton đều sẽ gồm 2 bước chung sau:
- Đặt phương thức khởi tạo mặc định (default constructor) là private để ngăn việc các đối tượng khác sử dụng toán tử new cho lớp Singleton
- Tạo 1 phương thức khởi tạo static hoạt động như 1 khởi tạo (constructor), trong đó phương thức này sẽ gọi private constructor để tạo ra 1 object và lưu nó vào trường static (static field). Tất cả các lệnh gọi sau đến phương thức này đều trả về đối tượng được lưu trong cached.
Nếu code của bạn truy cập đến lớp Singleton, nó có thể gọi đến phương thức static của Singleton, và sẽ luôn luôn được trả về chung 1 object.
Ví dụ thực tế
Singleton có nhiều ví dụ thực tế:
- 1 đất nước có thể chỉ có 1 chính phủ chính thức. Bất kể ai, cá nhân nào làm việc cho chính phủ thì danh xưng “Chính phủ” vẫn là 1 khái niệm chỉ định chung dùng để chỉ nhóm những người phụ trách. Chính phủ ở đây được xem như 1 ví dụ về Singleton.
- Khi bạn muốn tăng giảm âm lượng của 1 chiếc điện thoại. Bất kể có gọi từ phần mềm thứ 3, hay thao tác trực tiếp trên phần cứng; thì việc tăng giảm âm lượng cũng đều được thực hiện thông qua 1 phần điều khiển hệ thống. Ta cũng xem lớp điều khiển âm lượng này là 1 singleton
- Máy in cũng có thể xem là 1 singleton khi nó nhận yêu cầu và xử lý từ mọi người trong hệ thống.
Cấu trúc, cách triển khai Singleton
Lớp Singleton khai báo phương thức static getInstance trả về cùng 1 instance của nó.
Phương thức khởi tạo Singleton nên được ẩn khỏi code client, cách duy nhất để lấy đối tượng Singleton là gọi phương thức getInstance.
Triển khai Singleton Pattern code với TypeScript
/**
* The Singleton class defines the `getInstance` method that lets clients access
* the unique singleton instance.
*/
class Singleton {
private static instance: Singleton;
/**
* The Singleton's constructor should always be private to prevent direct
* construction calls with the `new` operator.
*/
private constructor() { }
/**
* The static method that controls the access to the singleton instance.
*
* This implementation let you subclass the Singleton class while keeping
* just one instance of each subclass around.
*/
public static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
/**
* Finally, any singleton should define some business logic, which can be
* executed on its instance.
*/
public someBusinessLogic() {
// ...
}
}
/**
* The client code.
*/
function clientCode() {
const s1 = Singleton.getInstance();
const s2 = Singleton.getInstance();
if (s1 === s2) {
console.log('Singleton works, both variables contain the same instance.');
} else {
console.log('Singleton failed, variables contain different instances.');
}
}
clientCode();