← Назад к вопросам

Что такое паттерн 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 для большей гибкости и тестируемости.

Что такое паттерн Singleton? | PrepBro