← Назад к вопросам
Что такое паттерн Singleton?
2.2 Middle🔥 232 комментариев
#Архитектура Flutter#ООП и паттерны
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Singleton паттерн — единственный экземпляр класса
Singleton — это порождающий паттерн проектирования, который гарантирует, что класс имеет только один экземпляр, и предоставляет глобальную точку доступа к этому экземпляру.
Основная идея
Singleton используется когда:
- Нужен только один экземпляр — база данных, логгер, конфигурация
- Глобальная точка доступа — все части приложения используют один объект
- Ленивая инициализация — объект создается при первом обращении
- Потокобезопасность — гарантированная безопасность в многопоточной среде
Простая реализация в Dart
class Database {
// Приватный конструктор
Database._internal();
// Статический экземпляр
static Database? _instance;
// Фабричный конструктор
factory Database() {
// Ленивая инициализация
_instance ??= Database._internal();
return _instance!;
}
void connect() {
print('Database connected');
}
}
// Использование
final db1 = Database();
final db2 = Database();
print(identical(db1, db2)); // true — один и тот же объект
Современный способ (Dart 3.0+)
class Logger {
static final Logger _instance = Logger._internal();
Logger._internal() {
print('Logger initialized');
}
factory Logger() => _instance;
void log(String message) {
print('[LOG] $message');
}
}
// Использование
final logger1 = Logger();
final logger2 = Logger();
logger1.log('Test'); // Инициализируется только один раз
Потокобезопасный Singleton
class AppConfig {
static final AppConfig _instance = AppConfig._internal();
static bool _initialized = false;
late String apiUrl;
late String appName;
AppConfig._internal() {
_initialize();
}
factory AppConfig() => _instance;
void _initialize() {
if (_initialized) return;
apiUrl = 'https://api.example.com';
appName = 'MyApp';
_initialized = true;
}
String getConfig(String key) {
return switch(key) {
'api_url' => apiUrl,
'app_name' => appName,
_ => 'Unknown key',
};
}
}
Практические примеры в Flutter
Singleton для управления аутентификацией:
class AuthService {
static final AuthService _instance = AuthService._internal();
String? _token;
String? _userId;
AuthService._internal();
factory AuthService() => _instance;
Future<void> login(String email, String password) async {
// Запрос к API
_token = 'generated_token';
_userId = 'user_123';
}
Future<void> logout() async {
_token = null;
_userId = null;
}
bool get isLoggedIn => _token != null;
String? get token => _token;
String? get userId => _userId;
}
// Использование в приложении
class LoginScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
final authService = AuthService();
await authService.login('user@example.com', 'password');
},
child: Text('Login'),
);
}
}
Singleton для API клиента:
class ApiClient {
static final ApiClient _instance = ApiClient._internal();
late final Dio _dio;
ApiClient._internal() {
_dio = Dio(
BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: Duration(seconds: 30),
),
);
}
factory ApiClient() => _instance;
Future<dynamic> get(String path) => _dio.get(path);
Future<dynamic> post(String path, {required dynamic data}) =>
_dio.post(path, data: data);
}
// Использование
class UserRepository {
final _apiClient = ApiClient();
Future<User> getUser(String id) async {
final response = await _apiClient.get('/users/$id');
return User.fromJson(response.data);
}
}
Singleton для SharedPreferences:
class PreferencesService {
static final PreferencesService _instance = PreferencesService._internal();
late SharedPreferences _prefs;
PreferencesService._internal();
factory PreferencesService() => _instance;
Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
}
void setString(String key, String value) {
_prefs.setString(key, value);
}
String? getString(String key) {
return _prefs.getString(key);
}
Future<bool> remove(String key) {
return _prefs.remove(key);
}
}
// В main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await PreferencesService().init();
runApp(MyApp());
}
Плюсы Singleton
- Контролируемый доступ — гарантирован единственный экземпляр
- Ленивая инициализация — объект создается при первом использовании
- Глобальная точка доступа — просто получить экземпляр
- Контроль над ресурсами — один объект = один набор ресурсов
Минусы Singleton
- Сложность тестирования — трудно создавать mock'и
- Скрытые зависимости — статический доступ скрывает зависимости
- Глобальное состояние — может привести к проблемам в многопоточности
- Нарушает SOLID — нарушает Single Responsibility и Dependency Inversion
Альтернативы
Dependency Injection (рекомендуется):
class UserRepository {
final ApiClient apiClient;
UserRepository(this.apiClient);
Future<User> getUser(String id) async {
return User.fromJson(await apiClient.get('/users/$id'));
}
}
// В main.dart
final apiClient = ApiClient();
runApp(MyApp(repository: UserRepository(apiClient)));
Service Locator (GetIt):
final getIt = GetIt.instance;
void setupServiceLocator() {
getIt.registerSingleton(ApiClient());
getIt.registerSingleton(AuthService());
}
// Использование
class MyWidget extends StatelessWidget {
final _apiClient = getIt<ApiClient>();
// ...
}
Когда использовать Singleton
- Database подключения
- Configuration management
- Logger
- Cache
- Analytics service
- API клиент (если нет DI)
Singleton — полезный паттерн, но его нужно использовать осторожно. В современном Flutter часто предпочитают Service Locator (GetIt) или Dependency Injection для большей гибкости и тестируемости.