Design Pattern | Singleton Pattern

Singleton thuộc Creational Pattern mô tả cách để tạo ra một đối tượng. Là một trong những ví dụ đơn giản của Design Pattern, tuy nhiên, bạn sẽ thấy nó là một kỹ thuật mạnh mẽ.

Như tên gọi của nó, Singleton Pattern là một cái gì đó duy nhất, chỉ có một. Nhưng nó đề cập đến cái gì ? Một điều duy nhất chúng ta muốn là gì ? Singleton Pattern đề cập đến việc chỉ có một đối tượng của một lớp. Giả sử như bạn đang lập trình mobile game Poker, hãy tưởng tượng trò chơi này có một lớp tùy chọn để lưu trữ tất cả các cài đặt cho người dùng. Những ưu tiên này bao gồm các yếu tố trực quan, chẳng hạn như màu sắc của bề mặt chơi và thiết kế của các quân bài. Hãy tưởng tượng nếu có nhiều hơn một lớp preferences tồn tại. Khi người dùng đang thiết lập tùy chọn của họ, lớp preferences nào đang lưu trữ lueaj chọn đó ? Và khi trò chơi đang được hiển thị, preferences nào được sử dụng để thiết kế hiển thị cho trò chơi ? Như bạn có thể thấy, việc có nhiều lớp preferences khác nhau không có nhiều ý nghĩa và có thể dẫn đến xung đột và mâu thuẫn.

Một một mục tiêu khác nữa của Singleton Pattern là có thể truy cập trong toàn bộ chương trình.

Chúng ta đã thảo luận về một mục tiêu thiêt kế chỉ có một thực thể (instance) của một lớp, điều này được thực hiện thực sự như thế nào ? Nếu bạn đang tự mình xây dựng một dự án nhỏ, sau khi bắt đầu class, bạn tạo một lưu ý cho mình để không tạo thêm một trường hợp khác, và vấn đề được giải quyết. Nếu bạn quên, và tạo một instance khác, thì bạn có thể gây đau đầu cho chính mình. Đây chắc chắn không phải giải pháp trong các dự án lớn hơn, hoặc không thể là giải pháp trong một dự án có nhiều nhà phát triển cùng tham gia. Chúng ta muốn ngăn các lỗi phát sinh từ nhiều đối tượng của lớp Singleton được khởi tạo. Giải pháp là xây dựng một điều này, và chỉ một điều này vào chính lớp đó, một instance khác của lớp Singleton. Bằng cách này, bạn mã hóa ý định thiết kế của mình trong phần mềm.

Làm thế nào để làm điều này ? Nếu bạn có một lớp với một hàm tạo (contructor) công khai, hàm tạo này có thể khởi tạo một đối tượng của lớp bất cứ lúc nào. Điều đó có nghĩa là không có gì ngăn cản một phần khác của phần mềm tạo ra một đối tượng khác của lớp này. Thay vào đó, thì bạn cung cấp cho lớp một hàm tạo riêng, để hàm tạo không thể được gọi từ bên ngoài lớp . Điều này tạo ra một mâu thuẫn rõ ràng, hàm tạo là riêng tư (private), vì vậy nó không thể gọi được từ bên ngoài lớp của nó. Vì vậy, làm thế nào để tạo một đối tượng của lớp này mà không có một hàm tạo công khai (public) ? Có hai  thành phần chính để giải quyết vấn đề này, đầu tiên khai báo một biến class được gọi là uniqueInstance, biến này sẽ tham chiếu đến một instance của lớp singleton của bạn. Bạn khai báo biến này là riêng tư, để nó chỉ có thể là được sửa đổi trong lớp. Bước tiếp theo là tạo một phương thức công khai sẽ tạo một instance của lớp này, nhưng chỉ khi một instance chưa từng tồn tại, gọi phương thức này là getInstance(), hàm này trước tiên sẽ kiểm tra xem biến uniqueInstance có null không, nếu nó là null, thì nó sẽ khởi tạo lớp và đặt biến này để tham chiếu đối tượng, nếu biến hiện tham chiếu đối tượng, thì phương thức sẽ trả về đối tượng đó. Vì phương thức getInstance() là công khia, nên nó gọi được ở mọi nơi của chương trình, và được sử dụng để tạo một instance của lớp. Và chúng ta vừa tạo ra Singleton Pattern, chúc mừng.


Bằng cách ẩn hàm tạo thông thường, và buộc các lớp khác phải gọi phương thức public getInstance(), chúng ta đã tạo ra thứ có gatekeeping, để đảm bảo rằng chỉ có một đối tượng của lớp được khởi tạo. Phương thức rất giống nhau được sử  dụng để tham chiếu toàn cục đối tượng duy nhất, nếu nó đã được tạo. Và từ bây giờ, chúng ta đã có thể sử dụng lớp singleton của mình cho các dự án nhỏ và lớn mà không phải lo lắng về nhiều trường hợp, gây nhầm lẫn cho nahf phát triển và tạo ra xung đột. Một ưu điểm khác của phiên bản này là đối tượng không được tạo ra cho đến khi thực sự cần thiết., điều này được gọi là Lazy Creational ( sự sáng tạo lười biếng), rất hữu ích với những đối tượng lớn. Nó không được tạo cho đến khi phương thức getInstance() được gọi, hiệu quả hơn.

Tất nhiên có sự đánh đổi khi sử dụng Singleton. Nếu có nhiều luồng máy tính đang chạy, có thể có các vấn đề gây ra bởi các luồng cố gắng truy cập vào đối tượng được chia sẻ.

Để quyết định xem Singleton, hay bất kỳ mẫu thiết kế (design pattern) nào khác, là thiết kế tốt nhất cho công việc, nó giúp có một ý tưởng tốt về cách thức hoạt động và hậu quả tiềm năng của nó. Các mẫu thiết kế được xác định bởi mục đích của chúng, vì vậy, bạn sẽ thấy các biến thể của Singleton được nhìn ra, nhưng mục tiêu của chúng luôn giống nhau, đó là để cấp quyền truy cập toàn cục vào một lớp bị giới hạn trong một instance. Nói chung, điều này đạt được bằng cách có một hàm tạo riêng  với một phương thức công khai khởi tạo lớp, nếu nó chưa được khởi tạo.

Ứng dụng của singleton thì nhiều, hãy tưởng tượng có nhiều chương trình mà bạn chỉ muốn một lần hiển thị tùy chọn của ứng dụng, như hàng đợi in cuả máy in của bạn, trình điều khiển thiết bị, nó có thể gây nhầm lẫn, thậm chí là thảm họa để có một bội số các trường hợp sinh ra. Mẫu singleton giới hạn một lớp cho một đối tượng bằng cách xây dựng ý định này vào code.

Và cuối cùng, hãy nói yêu Singleton, yêu design pattern, bởi cùng với tôi, bạn sẽ nâng cấp khả năng phát triển phần mềm của hai ta lên một tầm cao nữa. Cảm ơn.

Nhận xét

Bài đăng phổ biến từ blog này

Hiểu về Norm Regularization

Những thuật toán nền tảng trong lĩnh vực Trí tuệ nhân tạo (Artificial Intelligence I)