Singleton и его критика
Самый известный и самый спорный порождающий паттерн: один экземпляр на всю программу — и почему за это часто ругают.
Singleton гарантирует, что у класса есть только один экземпляр, и даёт глобальную точку доступа к нему.
Какую задачу решает
Иногда объект логически обязан быть один: пул соединений, конфигурация приложения, реестр. Создать второй такой объект — ошибка. Singleton переносит контроль за этим в сам класс: сколько раз ни вызывай конструктор, вернётся один и тот же экземпляр.
Идея и реализация
В Python самый чистый способ — переопределить __new__ и хранить единственный экземпляр в атрибуте класса.
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.data = {}
return cls._instance
a = Config()
a.data["theme"] = "dark"
b = Config() # тот же объект, не новый
print(b.data)
print("a is b:", a is b)
Вывод:
{'theme': 'dark'}
a is b: True
Оба имени a и b указывают на один объект, поэтому изменение через a видно через b. Альтернатива в Python — просто модуль: импортированный модуль и так существует в единственном экземпляре, и часто это лучший «синглтон».
Критика: почему его называют антипаттерном
- Глобальное состояние. Singleton — это глобальная переменная в маскировке. Любой код может незаметно его менять, связи становятся скрытыми.
- Тесты. Состояние тянется между тестами; подменить его на заглушку трудно. Это главный практический минус.
- Нарушает SRP. Класс отвечает и за свою логику, и за контроль собственного количества.
- Прячет зависимости. Метод, тайком берущий
Config(), не объявляет эту зависимость в сигнатуре.
Современная альтернатива — dependency injection: создать объект один раз в точке входа и передавать его явно тем, кому он нужен. Тогда вы сами решаете, когда он один, а тесты получают свою копию.
Где встречается
Логгеры (logging.getLogger возвращает один и тот же объект на имя), пулы соединений, кеши, объект приложения во многих фреймворках. Паттерн полезен, но применяйте его осознанно и закладывайте возможность подмены в тестах.
Итог
- Singleton = один экземпляр + глобальный доступ.
- В Python часто заменяется модулем.
- Главные минусы — скрытое глобальное состояние и боль в тестах; рассмотрите DI.