Ответ
Singleton часто критикуют как антипаттерн из-за проблем, которые он вносит в архитектуру приложения:
Основные недостатки:
- Глобальное состояние: Объект синглтона действует как глобальная переменная. Это усложняет модульное тестирование, так как состояние может сохраняться между тестами, делая их зависимыми друг от друга.
- Скрытые зависимости: Классы, использующие синглтон, не объявляют эту зависимость явно (например, через параметры конструктора), что нарушает принцип инверсии зависимостей и ухудшает читаемость кода.
- Нарушение принципа единственной ответственности (SRP): Singleton часто становится «божественным объектом», который знает и делает слишком много.
- Проблемы с многопоточностью: Наивная реализация небезопасна для потоков. Корректная реализация требует дополнительных усилий (например,
double-checked locking,holderилиenum).
Пример проблемной реализации:
public class NaiveSingleton {
private static NaiveSingleton instance;
private NaiveSingleton() {}
// НЕПОТОКОБЕЗОПАСНО! При одновременном вызове из двух потоков
// могут создаться два экземпляра.
public static NaiveSingleton getInstance() {
if (instance == null) {
instance = new NaiveSingleton();
}
return instance;
}
}
Альтернатива: Вместо явного синглтона предпочтительнее использовать внедрение зависимостей (DI). Контейнер (Spring, Guice) управляет жизненным циклом объекта, создавая его в единственном экземпляре (scope singleton) и явно внедряя туда, где это необходимо. Это сохраняет преимущество единого экземпляра, но устраняет недостатки антипаттерна.