Почему Singleton считается антипаттерном и какие у него проблемы
Что проверяют
Понимаете ли вы, за что именно ругают Singleton: не за единственный экземпляр, а за глобальную точку доступа и за то, что класс сам управляет своим жизненным циклом. Следом спрашивают, чем singleton-бин в Spring отличается от классического паттерна и как тестировать код, который внутри дёргает getInstance().
Как отвечать
Признайте, что «один экземпляр на приложение» это нормальное требование. Претензии к способу его выполнить:
- скрытая зависимость:
Registry.getInstance()в теле метода не видна ни в конструкторе, ни в сигнатуре, и состав зависимостей приходится вычитывать из кода; - глобальное изменяемое состояние: поменять его может кто угодно и когда угодно, а отладка превращается в поиск виновника;
- тесты: состояние переживает тест, порядок тестов начинает влиять на результат, параллельный прогон ломается. Подменять приходится через
mockStaticили рефлексию, а не передачей параметра; - потокобезопасность: ленивая инициализация без синхронизации даёт гонку, а double-checked locking корректен только с
volatileу поля; - жизненный цикл: экземпляр живёт до конца процесса. Пересоздать его между тестами, освободить ресурсы или держать два разных набора настроек нельзя.
Если экземпляр действительно один, назовите корректные реализации: enum с единственной константой (потокобезопасен по спецификации, устойчив к сериализации и рефлексии) либо статический вложенный класс-держатель с ленивой инициализацией.
Ключевой вывод: правильный ответ это внедрение зависимости. Пусть контейнер держит один экземпляр, а класс принимает его через конструктор и знает только интерфейс. Единственность сохраняется, а глобальный доступ и жёсткая связанность исчезают.
Где спотыкаются
Ругают Singleton общими словами про нарушение SOLID, не умея назвать конкретную боль: неотключаемое состояние в тестах или скрытую зависимость.
Пишут double-checked locking без volatile. Другой поток может увидеть ссылку на объект, конструктор которого ещё не отработал.
Утверждают, что singleton-бин в Spring это тот самый антипаттерн. Разница в том, что зависимость от бина передана явно через конструктор и подменяется в тесте одной строкой.