Singleton Pattern
Singleton Pattern là pattern thuộc nhóm create design pattern (nhóm khởi tạo) đảm bảo chỉ có 1 khởi tạo duy nhất của lớp, nó cung cấp điểm truy cập duy nhất đến khởi tạo này trên phạm vi toàn cục.
Last updated
Singleton Pattern là pattern thuộc nhóm create design pattern (nhóm khởi tạo) đảm bảo chỉ có 1 khởi tạo duy nhất của lớp, nó cung cấp điểm truy cập duy nhất đến khởi tạo này trên phạm vi toàn cục.
Last updated
Mẫu thiết kế Singleton Pattern giải quyết hai vấn đề trong cùng một thời gian, nó vi vạm nguyên tắc thức nhất trong SOLID - Single Responsibility Principle.
Single Responsibility Principle là nguyên tắc thiết kế lớp trong lập trình hướng đối tượng OOP, một lớp nên có một và duy nhất một chức năng lí do để nó tồn tại.
Đảm bảo rằng một lớp chỉ có 1 thể hiện duy nhất. Vì sao chúng ta phải kiểm soát số lượng khởi tạo của một lớp. Đơn giản nhất là muốn quản lý các truy cập đến các tài nguyên chia sẻ với nhau ví dụ như các file, database. Nếu ko quản lý sẽ rất dễ bị race condition - tranh chấp tài nguyên giữa các truy cập với nhau đến cùng 1 tài nguyên. Ý tưởng của pattern này khá đơn giản: hãy tưởng tượng ra bạn tạo ra 1 object, nhưng sau đó vì một chức năng khác cũng sử dụng class này, bạn lại tạo mới. Thay vì thế, chúng ta sẽ sử dụng lại object đã tạo ở ban đầu. Đối với các ngôn ngữ lập trình bậc cao, tạo một thể hiện mới của class - object rất đắt, hơn nữa khi sử dụng xong phải phải thu gom các object không sử dụng nữa lại, gây gánh nặng lên trình thu gom rác, vì vậy không nên tạo đối tượng khi không cần thiết.
Cung cấp điểm truy cập toàn cục cho khởi tạo: Khác với biến toàn cục - 1 dạng của singleton pattern, nó có thể bị ghi đè bời một đoạn code nào đấy. Đối với singleton pattern, nó cũng cấp truy cập đến khởi tạo đấy ở bất kì đâu trong mã của bạn, nhưng cũng hạn chế việc ghi đè lên đối tượng đã khởi tạo. Ngoài ra singleton còn việc giúp tối ưu mã tốt hơn, khi bạn không muốn mỗi nơi một kiểu khởi tạo object, tốt hơn hết là nên có 1 lớp đứng ra làm việc đấy.
Triển khai mẫu thiết kế singleton pattern này khá đơn giản, chỉ cần tuân thủ hai bước sau, các dạng triển khai khác là biến thể của pattern này.
Hàm khởi tạo mặc định của lớp phải để phạm vi truy cập là private, ngăn chặn việc khởi tạo đối tượng bằng từ khoá new
Tạo một hàm static có chức năng như một hàm tạo, khi hàm này được gọi, nó sẽ gọi đến private constructor đấy để khởi tạo đối tượng và lưu nào vào một field tĩnh nào đấy nằm trong lớp. Mọi kết nối sau này đều trả về đối tượng đã được tạo từ lần gọi đầu tiên được lưu trong biến tĩnh đấy. Vì thế mà bất cứ khi nào bạn muốn truy cập đến object đấy, thì luôn luôn được trả về object đã khởi tạo trước đó.
Ví dụ như một đất nước không thể có 2 chính phủ được. Chính phủ là 1 thể hiện duy nhất, cung cấp các dịch vụ cho mọi người dùng khi truy cập đến.
Một trong ví dụ tiêu biểu này đấy chính là DbContext của Entity Framework của dotnet. Đoạn mã sau tôi giả định sử dụng lớp Database và phương thức getInstance để trả về đối tượng DbContext sau lần gọi đầu tiên.
Sử dụng Singleton pattern đối với lớp DbContext trên, ngoài việc tăng tính đơn giản cho chương trình, bên cạnh đó còn đảm bảo tính consistency cho dữ liệu, đặc biệt trong cơ sở dữ liệu quan hệ (RDBSM). Nhất là trong các transaction giao dịch, việc đảm bảo việc nhất quán dữ liệu rất quan trọng. Giả sử có 2 instance của DbContext tại cùng 1 thời điểm cùng thao tác với 1 nguồn dữ liệu, rất dễ gấy ra tình trạng race condition, dữ liệu có thể bị sai lệch. Chính trong những trường hợp như vậy Singleton pattern phát huy được tắc dụng của nó. Tuy nhiên nó lại có nhược điểm khá là đau đớn, mời xem tiếp phần sau.
Sử dụng khi thực sự hiểu về nó. Singleton pattern được áp dụng khi bạn muốn quản lý số lượng instance của 1 lớp, giúp cho việc khời tạo đối tượng duy nhất một cách đơn giản hơn, tối ưu hoá mã nguồn.
Bạn luôn được cung cấp một thể hiện duy nhất của một lớp.
Bạn có thể truy cập trên phạm vi toàn cục đến đối tượng đã được khởi tạo trước đó.
Đối tượng chỉ thực sự được khởi tạo sau lần gọi đầu tiên (na ná giống Proxy Pattern).
Nó vi phạm nguyên tắc S - Single Responsibility Principle trong SOLID: một class - fun nên chỉ có 1 chức năng - nhiệm vụ duy nhất, sửa đổi cũng chỉ có một lí do duy nhất này. Trong hàm getInstance vừa cung cấp truy cấp đến đối tượng đã khởi tạo, vừa khởi tạo đối tượng đây trong hàm getInstance.
Che giấu đi cách khời tạo đối tượng, có thể gây khó khăn trong việc các đối tượng khác muốn biết về cách khởi tạo.
Trong lập trình đa luồng, việc áp dụng pattern cần các kĩ thuật đặc biệt để tránh việc khởi tạo đối tượng nhiều lần, các luồng cần share chung 1 resource, nhưng đến lúc đó cần phải cân nhắc việc tranh chấp sử dụng tài nguyên giữa các luồng cũng như tình trạng deadlock.
Việc có 1 instance đối với các hệ thống example có lẽ không quá ảnh hưởng, nhưng đối với các ứng dụng lớn, Singleton pattern lại không mang lại nhiều hiệu quả, 1 instance dễ gây ra tình trạng botlneck - tắc nghẽn. Một trong những giải pháp để giải quyết tình trạng này là Connection Pool, có lẽ tôi sẽ trình bày ở một chủ đề khác