Почему Singleton считается антипаттерном и какие у него проблемы
Что проверяют
Умеете ли вы отделить сам паттерн от способа его применения. В iOS синглтонов полно и в системных фреймворках, поэтому ответ «синглтон всегда плохо» слабый. Следом обычно просят показать, как вы сделаете код с shared тестируемым.
Как отвечать
В Swift синглтон выглядит как static let shared = NetworkManager() и приватный init. Создание здесь уже потокобезопасно: статические свойства инициализируются лениво и ровно один раз. Проблемы в другом:
- скрытые зависимости: класс, который внутри зовёт
NetworkManager.shared, не показывает в сигнатуре, от чего зависит. Понять, что он ходит в сеть, можно только прочитав весь код; - тесты: конкретный класс через
sharedне подменить моком, а состояние, накопленное в одном тесте, протекает в следующий; - глобальное изменяемое состояние: создание безопасно, а обращение к свойствам из разных потоков нет. В режиме Swift 6 компилятор ругается на
static let sharedс не-Sendableтипом, и приходится делать типactor, помечать его@MainActorили доказывать потокобезопасность; - время жизни: объект живёт всё время работы приложения, сбрасывать его, например при выходе пользователя из аккаунта, приходится вручную.
Как сгладить: объявите протокол и принимайте зависимость через init со значением по умолчанию, init(api: APIClient = NetworkManager.shared). Продакшен-код не меняется, а в тесте передаётся заглушка. Дальше можно перейти на DI-контейнер или Environment в SwiftUI.
Уместные случаи тоже назовите: URLSession.shared, FileManager.default, NotificationCenter.default. Это общие ресурсы без бизнес-состояния, и рядом всегда можно создать отдельный экземпляр.
Где спотыкаются
Главной проблемой называют многопоточное создание и рисуют double-checked locking из Java. В Swift static let эту задачу уже решает, а гонки возникают на изменяемых полях.
Говорят, что Apple синглтоны не использует. Используют, и часто в виде shared instance, где конструктор открыт и можно завести свой экземпляр.
Предлагают заменить синглтон глобальной переменной или статическими методами. Зависимость от этого остаётся такой же неявной и так же не подменяется в тестах.