Nội dung
Mục tiêu
Singleton là một mẫu thiết kế khởi tạo cho phép bạn đảm bảo rằng một lớp chỉ có một thể hiện, đồng thời cung cấp một điểm truy cập toàn cục cho thể hiện này.
Đặt vấn đề
Mẫu Singleton giải quyết hai vấn đề cùng một lúc và vi phạm Nguyên tắc Đơn Trách nhiệm:
1. Đảm bảo rằng một lớp chỉ có một cá thể duy nhất. Tại sao mọi người lại muốn kiểm soát số lượng cá thể mà một lớp có? Lý do phổ biến nhất cho điều này là kiểm soát quyền truy cập vào một số tài nguyên được chia sẻ — ví dụ, cơ sở dữ liệu hoặc tập tin.
Đây là cách nó hoạt động: hãy tưởng tượng rằng bạn đã tạo một đối tượng, nhưng sau một thời gian quyết định tạo một đối tượng mới. Thay vì nhận một đối tượng mới, bạn sẽ nhận một đối tượng mà bạn đã tạo.
Lưu ý rằng hành vi này không thể thực hiện với một hàm tạo thông thường vì lời gọi hàm tạo phải luôn trả về một đối tượng mới theo thiết kế.
2. Cung cấp một điểm truy cập toàn cục cho thể hiện đó. Hãy nhớ những biến toàn cục mà bạn (được rồi, tôi) đã sử dụng để lưu trữ một số đối tượng thiết yếu? Mặc dù chúng rất tiện dụng nhưng chúng cũng rất không an toàn vì bất kỳ mã nào cũng có khả năng ghi đè lên nội dung của các biến đó và làm hỏng chúng.
Cũng giống như một biến toàn cục, mẫu Singleton cho phép bạn truy cập một số đối tượng từ bất kỳ đâu trong chương trình. Tuy nhiên, nó cũng bảo vệ thể hiện đó không bị mã khác ghi đè.
Có một mặt khác của vấn đề này: bạn không muốn chương trình giải quyết vấn đề #1 bị phân tán khắp chương trình của mình. Tốt hơn nhiều nếu để nó trong một lớp, đặc biệt nếu phần còn lại của mã của bạn đã phụ thuộc vào nó.
Ngày nay, mẫu Singleton đã trở nên phổ biến đến mức mọi người có thể gọi một cái gì đó là singleton ngay cả khi nó chỉ giải quyết được một trong những vấn đề kể trên.
Giải pháp
Tất cả các triển khai của Singleton đều có hai bước chung sau:
- Đặt hàm tạo mặc định là private, để ngăn các đối tượng khác sử dụng toán tử new với lớp Singleton.
- Tạo một phương thức tĩnh hoạt động như một phương thức khởi tạo. Phương thức này gọi hàm tạo private để tạo một đối tượng và lưu nó trong một trường tĩnh. 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 trường tĩnh đó.
Nếu mã của bạn có quyền truy cập vào lớp Singleton thì nó có thể gọi phương thức tĩnh của Singleton. Vì vậy, bất cứ khi nào phương thức đó được gọi, cùng một đối tượng luôn được trả về.
Cấu trúc
Lớp Singleton khai báo phương thức tĩnh getInstance trả về cùng một thể hiện của lớp của nó.
Phương thức khởi tạo của Singleton phải được ẩn khỏi mã client. Gọi phương thức getInstance là cách duy nhất để lấy đối tượng Singleton.
Khả năng áp dụng
Sử dụng mẫu Singleton khi một lớp trong chương trình của bạn chỉ nên có một thể hiện duy nhất cho tất cả các client; ví dụ, một đối tượng cơ sở dữ liệu duy nhất được chia sẻ bởi các phần khác nhau của chương trình.
Sử dụng mẫu Singleton khi bạn cần kiểm soát chặt chẽ hơn đối với các biến toàn cục.
Ưu và nhược điểm
😄😄😄
Bạn có thể chắc chắn rằng một lớp chỉ có một cá thể duy nhất.
Bạn có được một điểm truy cập toàn cục vào thể hiện đó.
Đối tượng singleton chỉ được khởi tạo khi nó được yêu cầu lần đầu tiên.
🙁🙁🙁
Vi phạm Nguyên tắc Đơn Trách nhiệm. Mẫu đã giải quyết hai vấn đề tại thời điểm đó.
Mẫu Singleton có thể che đậy thiết kế xấu khi các thành phần của chương trình biết quá nhiều về nhau.
Mẫu yêu cầu xử lý đặc biệt trong môi trường đa luồng để nhiều luồng sẽ không tạo ra một đối tượng singleton nhiều lần.
Có thể khó thực hiện unit test của Singleton vì nhiều test framework dựa vào tính kế thừa khi tạo các đối tượng mock. Vì hàm tạo của lớp singleton là private và việc ghi đè các phương thức tĩnh là không thể trong hầu hết các ngôn ngữ, bạn sẽ cần phải nghĩ ra một cách sáng tạo để test singleton. Hoặc không viết test. Hoặc không sử dụng mẫu Singleton.
Mối quan hệ với các mẫu khác
- Một mẫu Facade thường có thể được chuyển đổi thành một Singleton vì một đối tượng facade duy nhất là đủ trong hầu hết các trường hợp.
- Flyweight sẽ giống với Singleton nếu bằng cách nào đó bạn có thể giảm tất cả các trạng thái được chia sẻ của các đối tượng xuống chỉ còn một đối tượng flyweight. Nhưng có hai điểm khác biệt cơ bản giữa các mẫu này:
- Chỉ có một thể hiện Singleton, trong khi Flyweight có thể có nhiều thể hiện với các trạng thái nội tại khác nhau.
- Singleton có thể thay đổi được. Flyweight là bất biến.
- Abstract Factory, Builder và Prototype đều có thể được triển khai dưới dạng các Singleton.
Để lại một bình luận