Приведи пример задачи, которую нельзя решить с помощью Singleton
Что проверяют
Понимаете ли вы границы применимости паттерна, а не заучили ли определение. Вопрос отсеивает тех, для кого Singleton это просто «класс с getInstance()»: интервьюер хочет услышать задачу, в которой единственный экземпляр мешает.
Как отвечать
Начните с того, что Singleton фиксирует ровно один экземпляр на область видимости, и именно это ограничение ломает целые классы задач:
- нужно несколько экземпляров с разной конфигурацией: пулы к двум базам, HTTP-клиенты с разными таймаутами, кэши с разным временем жизни. Единая точка доступа тут превращается в фабрику с параметром, то есть перестаёт быть Singleton;
- состояние на запрос или на пользователя. В веб-приложении один экземпляр делят все потоки, поэтому изменяемое поле с данными пользователя это гонка. Нужен
ThreadLocal, область видимости запроса или просто передача параметром; - масштабирование. «Один на приложение» означает один на JVM. Подняли второй инстанс, и счётчики, генераторы идентификаторов, кэши разъехались. Уникальность в кластере даёт последовательность в базе или внешнее хранилище, а не статическое поле;
- тестируемость. Статический вызов
Config.getInstance()зашит в код намертво, подменить его в тесте нечем, а состояние переживает соседние тесты, потому что класс в JVM не перезагружается.
Хороший ответ заканчивается заменой: зависимость передаётся через конструктор, а единственность обеспечивает контейнер или внешняя система.
Где спотыкаются
Подменяют вопрос: вместо задачи рассказывают про кривую реализацию, двойную проверку блокировки и забытый volatile. Это про корректность кода, а спрашивают про применимость.
Считают бин со временем жизни singleton тем же самым паттерном. Это один экземпляр на контейнер, создаёт его фреймворк, зависимости приходят снаружи, в тесте бин подменяется. Классический статический getInstance() не даёт ничего из этого.
Не замечают, что единственность кончается на границе процесса. Счётчик в статическом поле, блокировка «через synchronized» на распределённой операции, кэш без инвалидации на соседних узлах: всё это работает на одной машине и разваливается на двух.