Какой паттен чаще всего используешь?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Паттерны, которые я использую чаще всего
В моей повседневной разработке на 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 — управление зависимостями
- Остальные — для специфических задач
Лучшая практика — использовать столько паттернов, сколько нужно, но не больше. Излишняя архитектура — это тоже плохо.