Flutter 中 BLoC 与用例分离:清洁架构实战
Flutter 的 BLoC 模式常常演变成“上帝对象”:事件处理器混杂服务调用、验证和状态发射。这让维护、测试和扩展变得噩梦般艰难。将业务逻辑移入用例,能恢复正确的层级分离,遵循清洁架构原则:表示层(UI 和 BLoC)、领域层(用例、实体)和数据层(仓库、服务)。
用例实现特定场景:它协调服务、处理错误、应用业务规则,并保持与 UI 无关。BLoC 则专注单向数据流——处理事件并发射状态。
传统 BLoC:逻辑与 UI 纠缠不清
典型 BLoC 通过依赖注入直接调用服务,在处理器中执行副作用。下面是一个简化的 ItemsBloc 示例:
class ItemsBloc extends Bloc<ItemsEvent, ItemsState> {
final FetchItemsService _service;
ItemsBloc(this._service) : super(Initial()) {
on<FetchItemsEvent>(_onFetchItemsEvent);
}
Future<void> _onFetchItemsEvent(FetchItemsEvent event, Emitter<ItemsState> emit) async {
emit(Loading());
try {
final items = await _service.fetchItems();
emit(Loaded(items: items));
} catch (error) {
emit(Error(error: error));
}
}
}
问题:强耦合、难测试的代码,以及违反单一职责原则(SRP)。BLoC 知道太多数据、网络和业务逻辑细节。
重构后:BLoC 变身纯状态管理者
重构后,BLoC 只负责事件到状态的映射,无任何依赖:
class ItemsBloc extends Bloc<ItemsEvent, ItemsState> {
ItemsBloc() : super(Initial()) {
on<SetLoading>(_onSetLoading);
on<SetLoaded>(_onSetLoaded);
on<SetError>(_onSetError);
}
Future<void> _onSetLoading(SetLoading event, Emitter<ItemsState> emit) async {
emit(Loading());
}
Future<void> _onSetLoaded(SetLoaded event, Emitter<ItemsState> emit) async {
emit(Loaded(items: event.items));
}
Future<void> _onSetError(SetError event, Emitter<ItemsState> emit) async {
emit(Error(error: event.error));
}
}
用例负责协调:
class FetchItemsUseCase {
final ItemsBloc bloc;
final FetchItemsService service;
FetchItemsUseCase({required this.bloc, required this.service});
Future<void> call() async {
bloc.add(SetLoading());
try {
final items = await service.fetchItems();
bloc.add(SetLoaded(items: items));
} catch (error) {
bloc.add(SetError(error: error.toString()));
}
}
}
BLoC 缩减到 20 行,完全可测试:模拟事件并验证状态。
用例作为协调者的优势
用例不只是代理——它组合仓库数据、应用规则、记录活动并处理同步。它依赖抽象(接口),而非具体实现。
关键优势:
- 可测试性:为用例单元测试模拟服务/仓库。
- 可扩展性:新增场景无需改动 BLoC(开闭原则)。
- DI 灵活性:向用例注入服务;BLoC 保持独立。
- 可读性:业务场景隔离在专用类中。
真实项目中,此重构缩短开发时间 30%、减少 bug、简化测试。
数据流与权衡
UI → 用例 → 服务/仓库 → 事件至 BLoC → UI 状态。用例了解 BLoC(为简便的清洁架构妥协),但 UI 无需 bloc.add 调用。
BLoC 实现单向数据流:行为可预测、测试轻松。用例独立测试:模拟响应并验证调用。
核心要点
- 层级分离:BLoC 作状态管理者,用例管领域逻辑。
- SRP 实践:每个类只干一件事。
- 简化测试:BLoC 中纯函数,用例用模拟。
- 可扩展:兼容 Cubit、Riverpod 等其他状态管理器。
此方法适用于任何 BLoC 膨胀的 Flutter 项目。度量前后指标:类大小、测试覆盖率、功能交付时间。
— Editorial Team
暂无评论。