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

Какой паттен чаще всего используешь?

2.0 Middle🔥 271 комментариев
#State Management#Архитектура Flutter#ООП и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерны, которые я использую чаще всего

В моей повседневной разработке на Flutter я применяю несколько ключевых паттернов, которые помогают создавать масштабируемый и поддерживаемый код.

1. Паттерн Repository (самый используемый)

Это основной паттерн архитектуры, который я применяю в каждом проекте. Repository абстрагирует источник данных (API, БД, кеш) от бизнес-логики.

abstract class UserRepository {
  Future<List<User>> getUsers();
  Future<User> getUserById(String id);
  Future<void> updateUser(User user);
}

class UserRepositoryImpl implements UserRepository {
  final ApiClient apiClient;
  final LocalDatabase database;
  
  UserRepositoryImpl(this.apiClient, this.database);
  
  @override
  Future<List<User>> getUsers() async {
    try {
      // Сначала попробовать получить из кеша
      final cached = await database.getCachedUsers();
      if (cached.isNotEmpty) return cached;
      
      // Если нет — загрузить с API
      final users = await apiClient.fetchUsers();
      await database.cacheUsers(users);
      return users;
    } catch (e) {
      // Вернуть из кеша, даже если есть ошибка
      return await database.getCachedUsers();
    }
  }
}

Почему это лучше всего:

  • Легко тестировать (можно подменить реализацию)
  • Легко менять источник данных без изменения UI
  • Один ответственный класс за получение данных

2. BLoC/Cubit паттерн

Для управления состоянием приложения использую BLoC (Business Logic Component) или его более простого наследника Cubit.

class UserCubit extends Cubit<UserState> {
  final UserRepository repository;
  
  UserCubit(this.repository) : super(UserInitial());
  
  Future<void> fetchUsers() async {
    emit(UserLoading());
    try {
      final users = await repository.getUsers();
      emit(UserLoaded(users));
    } catch (e) {
      emit(UserError(e.toString()));
    }
  }
}

// В UI
BlocBuilder<UserCubit, UserState>(
  builder: (context, state) {
    if (state is UserLoading) return LoadingWidget();
    if (state is UserLoaded) return UserList(state.users);
    if (state is UserError) return ErrorWidget(state.error);
    return SizedBox.shrink();
  },
)

Преимущества:

  • Четкое разделение бизнес-логики и UI
  • Состояние управляется централизованно
  • Легко тестировать бизнес-логику отдельно

3. Factory паттерн

Использую для создания сложных объектов, особенно в процессе инициализации приложения.

abstract class ServiceFactory {
  static T create<T>() {
    switch (T) {
      case ApiClient:
        return ApiClient(
          baseUrl: Environment.apiUrl,
          timeout: Duration(seconds: 30),
        ) as T;
      case UserRepository:
        return UserRepositoryImpl(
          apiClient: create<ApiClient>(),
          database: create<LocalDatabase>(),
        ) as T;
      default:
        throw 'Cannot create $T';
    }
  }
}

// Использование
final apiClient = ServiceFactory.create<ApiClient>();

4. Observer/Listener паттерн

Для реактивного программирования использую слушатели состояния.

class NotificationService {
  final List<NotificationListener> _listeners = [];
  
  void subscribe(NotificationListener listener) {
    _listeners.add(listener);
  }
  
  void notify(Notification notification) {
    for (var listener in _listeners) {
      listener.onNotification(notification);
    }
  }
}

abstract class NotificationListener {
  void onNotification(Notification notification);
}

5. Singleton паттерн (через GetIt)

Для глобальных сервисов, которые должны быть только одни на все приложение.

final getIt = GetIt.instance;

void setupServiceLocator() {
  // Синглтоны
  getIt.registerSingleton<ApiClient>(ApiClient());
  getIt.registerSingleton<SharedPreferences>(...);
  
  // Фабрики (создаются новые объекты каждый раз)
  getIt.registerFactory<UserBloc>(() => UserBloc(getIt<UserRepository>()));
}

// Использование в любом месте
final apiClient = getIt<ApiClient>();

6. Builder паттерн

Для создания сложных UI виджетов с множеством параметров.

class ButtonBuilder {
  late String text;
  late VoidCallback onPressed;
  ButtonSize size = ButtonSize.medium;
  ButtonVariant variant = ButtonVariant.primary;
  bool enabled = true;
  
  ButtonBuilder withText(String text) {
    this.text = text;
    return this;
  }
  
  ButtonBuilder withSize(ButtonSize size) {
    this.size = size;
    return this;
  }
  
  Button build() {
    return Button(
      text: text,
      onPressed: onPressed,
      size: size,
      variant: variant,
      enabled: enabled,
    );
  }
}

// Использование
final button = ButtonBuilder()
  .withText('Click me')
  .withSize(ButtonSize.large)
  .build();

7. Adapter паттерн

Для преобразования несовместимых интерфейсов.

// Старый API
class OldUserApi {
  Map<String, dynamic> getUserData(String id) { ... }
}

// Новый интерфейс
abstract class UserProvider {
  Future<User> getUser(String id);
}

// Адаптер
class OldUserApiAdapter implements UserProvider {
  final OldUserApi oldApi;
  
  OldUserApiAdapter(this.oldApi);
  
  @override
  Future<User> getUser(String id) async {
    final data = oldApi.getUserData(id);
    return User.fromMap(data);
  }
}

8. Strategy паттерн

Для выбора различных алгоритмов в runtime.

abstract class SortingStrategy {
  List<T> sort<T>(List<T> items);
}

class QuickSortStrategy extends SortingStrategy {
  @override
  List<T> sort<T>(List<T> items) { ... }
}

class MergeSortStrategy extends SortingStrategy {
  @override
  List<T> sort<T>(List<T> items) { ... }
}

class DataProcessor {
  SortingStrategy _strategy;
  
  DataProcessor({required SortingStrategy strategy}) : _strategy = strategy;
  
  void processData(List items) {
    final sorted = _strategy.sort(items);
    // ...
  }
}

Почему именно эти паттерны

Причины выбора:

  • Repository — основа любой архитектуры, абстрагирует данные
  • BLoC/Cubit — стандарт состояния в Flutter экосистеме
  • Dependency Injection через GetIt — управление зависимостями
  • Factory — создание сложных объектов
  • Observer — реактивное программирование

Практический пример комбинирования паттернов

// Инициализация (Factory + Singleton)
void main() {
  setupServiceLocator();
  runApp(MyApp());
}

// В приложении (Repository + BLoC)
final userRepository = getIt<UserRepository>();
final userCubit = UserCubit(userRepository);

// В UI (BLoC Pattern)
BlocProvider(
  create: (context) => getIt<UserCubit>(),
  child: UserScreen(),
)

Итог

В реальном проекте не использую паттерны ради паттернов. Каждый применяю потому, что он решает конкретную проблему:

  • Repository — инкапсуляция доступа к данным
  • BLoC — управление состоянием
  • DI — управление зависимостями
  • Остальные — для специфических задач

Лучшая практика — использовать столько паттернов, сколько нужно, но не больше. Излишняя архитектура — это тоже плохо.

Какой паттен чаще всего используешь? | PrepBro