Singleton là một mẫu thiết kế phổ biến đảm bảo rằng một lớp chỉ có duy nhất một thể hiện (instance) được tạo ra trong suốt vòng đời của ứng dụng. Dưới đây là các phương pháp khác nhau để hiện thực hóa mẫu này trong Python.
1. Ghi đè phương thức __new__
Phương pháp tiêu chuẩn nhất là ghi đè phương thức khởi tạo __new__. Tại đây, chúng ta kiểm tra xem thể hiện đã tồn tại chưa trước khi tạo mới.
class UniqueInstance:
_registry = None
def __new__(cls):
if cls._registry is None:
cls._registry = super().__new__(cls)
return cls._registry
obj_x = UniqueInstance()
obj_y = UniqueInstance()
obj_z = UniqueInstance()
print(obj_x, id(obj_x))
print(obj_y, id(obj_y))
print(obj_z, id(obj_z))
Kết quả thực thi sẽ cho thấy cả ba biến đều trỏ về cùng một địa chỉ bộ nhớ.
2. Mẫu Monostate (Borg)
Khác với Singleton truyền thống, mẫu Monostate cho phép tạo nhiều thể hiện nhưng tất cả đều chia sẻ cùng một trạng thái nội bộ bằng cách gán chung __dict__.
class Monostate:
_shared_data = {}
def __new__(cls, *args, **kwargs):
obj = super().__new__(cls, *args, **kwargs)
obj.__dict__ = cls._shared_data
return obj
class ConfigManager(Monostate):
version = 1.0
cfg1 = ConfigManager()
cfg2 = ConfigManager()
cfg3 = ConfigManager()
cfg1.version = 2.0
ConfigManager.version = 3.5
print(id(cfg1), id(cfg2), id(cfg3))
print(cfg1.version, cfg2.version, cfg3.version)
Dù các đối tượng có id khác nhau, việc thay đổi thuộc tính trên một đối tượng sẽ ảnh hưởng đến tất cả các đối tượng khác do chúng dùng chung từ điển thuộc tính.
3. Sử dụng Decorator
Có thể sử dụng một hàm decorator để bao bọc lớp, quản lý việc khởi tạo thông qua một từ điển lưu trữ cache.
def register_single(cls):
store = {}
def obtain(*args, **kwargs):
if cls not in store:
store[cls] = cls(*args, **kwargs)
return store[cls]
return obtain
@register_single
class DatabaseConnection:
status = "connected"
db1 = DatabaseConnection()
db2 = DatabaseConnection()
db3 = DatabaseConnection()
print(id(db1), id(db2), id(db3))
print(db1.status, db2.status, db3.status)
Decorator này đảm bảo rằng hàm obtain luôn trả về cùng một thể hiện đã được tạo lần đầu tiên.
4. Factory Method qua Class Method
Một cách tiếp cận khác là sử dụng phương thức lớp (classmethod) như một nhà máy để kiểm soát việc khởi tạo đối tượng.
class ServiceLocator:
_service = None
@classmethod
def acquire(cls):
if cls._service is None:
cls._service = cls()
return cls._service
svc1 = ServiceLocator.acquire()
svc2 = ServiceLocator.acquire()
svc3 = ServiceLocator.acquire()
svc4 = ServiceLocator.acquire()
print(svc1, id(svc1))
print(svc2, id(svc2))
print(svc3, id(svc3))
print(svc4, id(svc4))
Phương thức acquire sẽ kiểm tra biến lớp riêng tư. Nếu chưa có instance nào, nó sẽ tạo mới và lưu trữ để sử dụng cho các lần gọi sau.