Синглтон (Singleton) - это паттерн проектирования, который гарантирует, что у класса будет только один экземпляр на протяжении всего жизненного цикла приложения, и предоставляет к нему глобальную точку доступа.
Данный паттерн является одним из самых популярных и одновременно спорных паттернов в
iOS разработке.
Вот как это работает на практике:
Когда вы создаёте синглтон, вы делаете его конструктор приватным, чтобы предотвратить создание дополнительных экземпляров извне, а единственный экземпляр храните в статическом свойстве.
Swift автоматически обеспечивает ленивую инициализацию - объект создаётся только при первом обращении к этому свойству.
Например, если у вас есть класс
AppConfig, который управляет настройками приложения, его можно реализовать как синглтон:
final class AppConfig {
static let shared = AppConfig()
private init() {}
var apiKey: String = "12345"
}
Такой подход удобен для объектов, которые должны быть доступны из разных частей программы, но при этом не должны дублироваться - например, для работы с настройками, сетевыми запросами или аналитикой.
В нашем коде есть серьезный недостаток, если в будущем понадобится подменить
apiKey для тестов, возникнут сложности. Более гибкий вариант: использование протоколов:
protocol ConfigService {
var apiKey: String { get }
}
final class ProdConfig: ConfigService {
static let shared = ProdConfig()
private init() {}
let apiKey = "prod_12345"
}
class TestConfig: ConfigService {
let apiKey = "test_67890"
}
Теперь в проде используем
ProdConfig.shared, а в тестах можем подсунуть объект
TestConfig.
Главная проблема синглтонов:
Они создают неявные зависимости. Представьте, у вас 20 классов, использующих
AppConfig.shared. Вдруг понадобится сделать разные конфиги для разных пользователей. Придется переписывать половину приложения.
Поэтому я использую синглтоны только для логгеров и аналитики. Для всего остального лучше подходит
dependency injection, потому что это делает зависимости прозрачными и даёт гибкость в управлении объектами.
Пример:
protocol APIServiceProtocol {
func fetchData()
}
class APIService: APIServiceProtocol {
func fetchData() {
}
}
class DataManager {
let apiService: APIServiceProtocol
init(apiService: APIServiceProtocol) {
self.apiService = apiService
}
func loadData() {
apiService.fetchData()
}
}
Вывод:
Синглтон - мощный инструмент, но при неправильном использовании может принести больше вреда, чем пользы. Главная его опасность в том, что он создаёт скрытые зависимости, которые со временем превращают код в запутанный клубок. Лучше всего его использовать только для объектов, которые действительно должны существовать в единственном экземпляре.