You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dart中Clean架构下用单方法接口实现依赖倒置的最优方式

问题背景

我正在实现Clean Architecture,因此需要使用用例(use cases)。最初定义了如下抽象接口类:

abstract interface class UploadAssetUseCase{
  Future<(AssetUploadFailure?, Url?)> execute(Uint8List data);
}

Dart分析器抛出警告:

不必要地使用抽象类。尝试将'execute'设为顶层函数并移除该类。

于是我改用typedef方式:

typedef UploadAssetUseCase=  Future<(AssetUploadFailure?, Url?)> Function(Uint8List data);

这种方式具备可测试性,但需要额外创建抽象类用于Mock。不过当尝试实现它时:

class FirebaseStorageUploadAssetUseCase implements UploadAssetUseCase{
  
}

会抛出异常,因为UploadAssetUseCase是函数类型无法被实现。如果回到可实现的抽象类,又会触发之前的警告:

abstract interface class _IUploadAssedUseCase {//出现之前试图避免的警告
  Future<(AssetUploadFailure?, Url?)> call(
    Uint8List document,
  );
}

class FirebaseStorageUploadAssetUseCase implements _IUploadAssedUseCase{
  const FirebaseStorageUploadAssetUseCase(this.storage);
  final FirebaseStorage storage;

  @override
  Future<(AssetUploadFailure?, Url?)> call(
    Uint8List document,
  ){
   
  }
}

请问在此场景下最优方案是什么?是否应忽略警告继续使用类而非顶层函数?还有其他可行方法吗?

我的analysis options 3.1.0配置如下:

analyzer:
  errors:
    invalid_use_of_visible_for_testing_member: error
    const_eval_throws_exception: ignore
linter:
  rules:
    public_member_api_docs: false
    library_private_types_in_public_api: false
    lines_longer_than_80_chars: false

解决方案

针对Clean Architecture中用例的场景,这里有几种实用方案:

1. 忽略警告或禁用对应lint规则

Clean Architecture的核心是依赖抽象,用抽象类定义用例接口完全合理。Dart分析器的警告只是针对单一方法抽象类的通用建议,并不适配架构设计场景。

  • 临时忽略单类警告:在抽象类上添加注释
    // ignore: unnecessary_abstract_class
    abstract interface class UploadAssetUseCase{
      Future<(AssetUploadFailure?, Url?)> execute(Uint8List data);
    }
    
  • 全局禁用规则:在analysis_options.yaml中添加配置,避免每个类都加注释
    linter:
      rules:
        # 保留原有规则...
        unnecessary_abstract_class: false
    

2. 使用typedef结合类的call方法实现

typedef无法被implements,但可以让类实现call方法匹配函数签名,既符合函数类型定义,又能通过类封装依赖(比如FirebaseStorage):

typedef UploadAssetUseCase = Future<(AssetUploadFailure?, Url?)> Function(Uint8List data);

class FirebaseStorageUploadAssetUseCase {
  const FirebaseStorageUploadAssetUseCase(this.storage);
  final FirebaseStorage storage;

  // 实现与typedef匹配的call方法
  Future<(AssetUploadFailure?, Url?)> call(Uint8List data) async {
    // 具体上传逻辑
  }
}

使用时直接把类实例当作函数调用:

final uploadUseCase = FirebaseStorageUploadAssetUseCase(FirebaseStorage.instance);
final result = await uploadUseCase(data);

测试时可以直接Mock该函数类型,或创建测试实现类并实现call方法。

3. 保留抽象类并改用call方法

如果更倾向于用接口约束,保留抽象类并使用call方法,既能让类实例像函数一样调用,又符合Clean Architecture的抽象依赖原则:

// ignore: unnecessary_abstract_class
abstract interface class UploadAssetUseCase {
  Future<(AssetUploadFailure?, Url?)> call(Uint8List data);
}

class FirebaseStorageUploadAssetUseCase implements UploadAssetUseCase {
  const FirebaseStorageUploadAssetUseCase(this.storage);
  final FirebaseStorage storage;

  @override
  Future<(AssetUploadFailure?, Url?)> call(Uint8List data) async {
    // 实现逻辑
  }
}

最优方案建议

对于Clean Architecture场景,方案1或方案3更贴合架构设计思想:抽象类能明确定义用例契约,后续扩展(比如新增方法、修改签名)时,所有实现类都会收到编译提示。若团队统一遵循Clean Architecture规范,全局禁用unnecessary_abstract_class规则是最省心的选择。

内容的提问来源于stack exchange,提问作者Luis Utrera

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 04:35:24