为所有用例定义统一接口是否合理?流返回场景该如何处理?
关于Usecase接口设计的疑问解答
问题背景
我已定义了一个返回Future的Usecase接口,请问为所有用例统一使用该接口是否为合理方案?此外,若存在需要返回Stream的用例(例如监听Firebase Cloud Firestore集合快照),是否需要定义两个独立接口?
现有代码实现
import 'package:equatable/equatable.dart'; import '../errors/failure.dart'; import 'package:dartz/dartz.dart'; abstract class Usecase<Input, Output> { Future<Either<Failure, Output>> call(Input input); } class Input<T> extends Equatable { final T parameter; Input(this.parameter); @override List<Object?> get props => [parameter]; } class NoInput extends Equatable { @override List<Object?> get props => []; }
问题解答
1. 统一使用返回Future的Usecase接口是否合理?
完全合理,原因如下:
- 大部分业务用例属于单次执行的操作(比如获取单条数据、提交表单、触发一次性业务逻辑),
Future<Either<Failure, Output>>的结构能完美适配这类场景:用Future表示异步执行,Either统一封装成功结果和失败错误,符合Clean Architecture中用例层的错误处理规范。 - 你定义的
Input/NoInput类已经统一了参数传递逻辑,避免了不同用例参数处理的混乱,进一步强化了接口的通用性。 - 这种统一接口的设计能降低上层调用的学习成本,所有用例的调用方式一致(
await usecase(input)),便于统一封装日志、异常捕获等横切关注点。
2. 返回Stream的用例是否需要独立接口?
建议定义独立的StreamUsecase接口,理由如下:
- 语义差异:
Future代表单次异步结果,Stream代表持续的数据流,两者的业务场景完全不同(单次请求 vs 实时监听),分开接口能让代码意图更清晰。 - 使用方式不同:调用返回
Future的用例用await,而Stream需要监听(listen/await for),如果强行复用同一个接口,要么返回Future<Stream<Either<Failure, Output>>>,会增加不必要的嵌套复杂度;要么破坏接口的单一职责。 - 符合SOLID原则:单一职责原则要求每个类/接口只负责一种类型的逻辑,分开接口能让
Usecase专注于单次操作,StreamUsecase专注于实时数据流。
示例:StreamUsecase接口定义
abstract class StreamUsecase<Input, Output> { Stream<Either<Failure, Output>> call(Input input); }
监听Firestore集合快照的用例可实现该接口:
class ListenFirestoreCollectionUsecase extends StreamUsecase<CollectionInput, List<Document>> { final FirestoreRepository repository; ListenFirestoreCollectionUsecase(this.repository); @override Stream<Either<Failure, List<Document>>> call(CollectionInput input) { return repository.listenCollection(input.collectionPath); } }
补充说明
如果想尽量减少接口数量,也可以尝试用泛型抽象返回类型,但会增加泛型复杂度,比如:
abstract class Usecase<Input, Output, R extends FutureOr<Stream<Either<Failure, Output>>>> { R call(Input input); }
但这种方式会让接口的使用变得晦涩,不如分开两个接口直观,不推荐在业务代码中使用。
内容的提问来源于stack exchange,提问作者djalmafreestyler
相关产品推荐
相关产品推荐

