Trong ngành công nghiệp phần mềm, mẫu thiết kế Singleton được sử dụng để giới hạn việc tạo ra một lớp đối tượng chỉ một lần. Điều này rất quan trọng khi cần một đối tượng duy nhất để điều phối các hoạt động trong toàn bộ hệ thống. Khái niệm này cũng có thể được mở rộng để áp dụng cho các hệ thống yêu cầu chỉ có một đối tượng tồn tại hoặc giới hạn số lượng đối tượng. Thuật ngữ này bắt nguồn từ khái niệm toán học về singleton.
Mặc dù mẫu Singleton rất phổ biến, nhưng cũng có nhiều ý kiến phê phán cho rằng nó đi ngược lại nguyên tắc hướng đối tượng. Điều này là vì nó thường được áp dụng trong những tình huống mà một đối tượng duy nhất không thực sự cần thiết, hoặc tạo ra những hạn chế không cần thiết. Mẫu thiết kế Singleton [4] là một trong số 23 mẫu thiết kế nổi tiếng của GoF, giúp giải quyết các vấn đề thiết kế lặp lại để tạo ra phần mềm linh hoạt, dễ thay đổi, thử nghiệm và tái sử dụng.
Những vấn đề mà các mẫu Singleton thiết kế có thể giải quyết bao gồm: [5]
Làm thế nào để đảm bảo rằng một lớp chỉ có một đối tượng duy nhất?
Như thế nào để dễ dàng truy cập vào một thể hiện duy nhất của lớp?
Những cách nào giúp một lớp quản lý việc tạo ra các thể hiện của nó?
Làm sao để giới hạn số lượng thể hiện (instance) của một lớp?
Các vấn đề mà mẫu thiết kế Singleton giải quyết bao gồm:
Ẩn đi constructor của lớp.
Xác định một phương thức tĩnh công khai (getInstance()) để trả về thể hiện duy nhất của lớp.
Ý tưởng cốt lõi của mô hình này là lớp đó tự quản lý việc khởi tạo của chính nó, đảm bảo chỉ được khởi tạo một lần.
Việc ẩn constructor đảm bảo rằng lớp singleton không thể được khởi tạo từ bên ngoài.
Các phương thức tĩnh có thể được gọi dễ dàng thông qua tên lớp và tên phương thức (Singleton.getInstance()).
Ứng dụng chung
Các mẫu abstract factory, builder và prototype có thể tích hợp Singletons trong quá trình thực hiện.
Facade thường áp dụng mẫu singleton.
Singleton thường được sử dụng để quản lý biến toàn cục.
Singleton thường được ưa chuộng hơn biến toàn cục vì:
- Loại bỏ các biến không cần thiết, chỉ quản lý các biến cần thiết cho ứng dụng.
- Singleton chỉ sử dụng một lượng tài nguyên hạn chế, trong khi biến toàn cục thường được tạo ra từ nhiều ngôn ngữ hoặc kiểu dữ liệu phức tạp, dẫn đến tiêu tốn tài nguyên nhiều hơn.
Ứng dụng của nó
Khi chỉ cần một trường hợp duy nhất hoặc một số trường hợp cụ thể của một lớp. Đối tượng Facade thường là Singleton vì ứng dụng chỉ cần một đối tượng Facade duy nhất.
Cách thực hiện
Để triển khai mô hình Singleton, cần đảm bảo:
Chỉ có một instance duy nhất của lớp Singleton được tạo ra.
Cung cấp quyền truy cập công khai cho trường hợp đó.
Thông thường, điều này được thực hiện bằng cách:
Đảm bảo rằng tất cả các constructor của lớp đều là private.
Cung cấp một phương thức tĩnh để trả về tham chiếu đến đối tượng.
Thông thường, đối tượng được lưu trữ dưới dạng một biến tĩnh riêng biệt; đối tượng được khởi tạo khi biến được gán giá trị, trước khi phương thức tĩnh được gọi lần đầu tiên. Dưới đây là ví dụ bằng Java.
public final class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } }
Khởi tạo trì hoãn Một singleton có thể được triển khai bằng cách sử dụng khởi tạo trì hoãn, nơi đối tượng được tạo ra khi phương thức tĩnh được gọi lần đầu tiên. Nếu phương thức tĩnh có thể được gọi từ nhiều luồng đồng thời, cần có các biện pháp để ngăn ngừa các điều kiện có thể dẫn đến việc tạo ra nhiều đối tượng. Dưới đây là một ví dụ về cách triển khai an toàn, sử dụng khóa kiểm tra kép trong Java:
public final class Singleton { private static volatile Singleton instance = null; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
