Triển Khai Mẫu Thiết Kế Singleton Trong Python

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.

Thẻ: python singleton-pattern oop metaprogramming

Đăng vào ngày 26 tháng 9 lúc 21:48