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.
Проверьте себя
1. Что гарантирует Singleton?
AПотокобезопасность любого метода
BЕдинственный экземпляр класса и глобальный доступ к нему
CБыстрое создание объектов
DНеизменяемость данных
2. Почему Singleton часто критикуют?
AОн слишком быстрый
BОн вводит скрытое глобальное состояние и усложняет тестирование
CОн требует много памяти
DОн работает только в Java
3. Какая идиоматичная альтернатива Singleton есть в Python?
AГлобальный список
BМодуль (он и так в единственном экземпляре)
CДекоратор @staticmethod
DКортеж
Поддержать проект