Добро пожаловать в руководство по использованию паттерна Singleton в языке программирования Dart. Этот паттерн проектирования является одним из самых распространённых и простых, но при этом невероятно полезным инструментом в арсенале разработчика.
Singleton (Одиночка) — это паттерн проектирования, который гарантирует наличие только одного экземпляра класса и предоставляет глобальную точку доступа к нему. Проще говоря: сколько бы раз вы ни пытались создать объект Singleton, вы всегда будете получать один и тот же экземпляр.
Паттерн Singleton решает несколько распространённых задач:
В Dart существует несколько способов реализации паттерна Singleton. Разберем два наиболее популярных:
class AppSettings {
// Приватный статический экземпляр (eager initialization)
static final AppSettings _instance = AppSettings._internal();
// Фабричный конструктор
factory AppSettings() {
return _instance;
}
// Приватный конструктор
AppSettings._internal();
// Настройки приложения
bool isDarkMode = false;
String language = 'ru';
double fontSize = 14.0;
void saveSettings() {
print('Настройки сохранены: тема = $isDarkMode, язык = $language, шрифт = $fontSize');
}
}
Особенности:
class DataCache {
// Приватный статический экземпляр (lazy initialization)
static DataCache? _instance;
// Геттер для получения экземпляра
static DataCache get instance {
_instance ??= DataCache._internal();
return _instance!;
}
// Приватный конструктор
DataCache._internal();
// Кеш данных
final Map<String, dynamic> _cache = {};
Future<dynamic> getData(String key) async {
if (_cache.containsKey(key)) {
print('Получение из кеша: $key');
return _cache[key];
}
print('Загрузка данных: $key');
await Future.delayed(Duration(seconds: 2)); // эмуляция задержки
final data = 'Данные для $key (${DateTime.now()})';
_cache[key] = data;
return data;
}
void clearCache() {
_cache.clear();
print('Кеш очищен');
}
}
Особенности:
Тип | Когда создаётся | Плюсы | Минусы |
---|---|---|---|
Eager | При старте программы | Простота, надёжность | Занимает память сразу |
Lazy | При первом использовании | Экономия ресурсов | Нужен осторожный код при многопоточности |
Применение Singleton оправдано в следующих случаях:
Несмотря на удобство, Singleton имеет недостатки:
Глобальное состояние Усложняет понимание работы приложения и его тестирование.
Жёсткая связанность компонентов Модули напрямую зависят от конкретной реализации Singleton.
Трудности с модульным тестированием Подменить Singleton на мок-объект непросто без дополнительных абстракций.
Потенциальные утечки памяти Singleton "живет" всё время работы приложения — объекты внутри него никогда не удаляются.
Сложность масштабирования В многопоточной среде (например, при работе с isolates) Singleton нужно реализовывать осторожно.
Паттерн Singleton — мощный инструмент в разработке приложений. Dart делает его реализацию особенно элегантной благодаря фабричным конструкторам и возможностям ленивой инициализации.
Главное правило: Использовать Singleton там, где это оправдано, а не по умолчанию!
Базовый уровень:
Создайте класс Logger
с использованием Singleton.
Методы:
log(String message)
: пишет сообщение в консоль и сохраняет его в список истории.clear()
: очищает историю.history
: возвращает все сохранённые сообщения.(Подсказка: используйте List
Средний уровень:
Реализуйте класс UserSession
для хранения информации о текущем пользователе.
Методы:
login(String username)
logout()
isLoggedIn()
(Подсказка: сохраняйте имя пользователя в поле класса.)
Продвинутый уровень:
Создайте менеджер загрузки изображений ImageLoader
.
Методы:
load(String url)
: загружает изображение (если уже есть в кеше — возвращает кешированное).clearCache()
cacheStats()
: количество кешированных изображений.(Подсказка: используйте Map<String, dynamic> для кеша.)
Экспертный уровень:
Реализуйте DatabaseConnectionManager
с поддержкой двух типов подключений:
primary
)readReplica
)Используйте абстрактную фабрику для создания нужного подключения.
(Подсказка: определите enum ConnectionType
.)