dart-api-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Dart Effective Design

Dart 有效设计

Goal

目标

Analyzes and refactors Dart code to enforce idiomatic API design, strict type safety, and robust object-oriented hierarchies. Applies Dart 3 class modifiers to control extension and implementation boundaries, ensures proper encapsulation through getters and setters, and standardizes naming conventions for predictable, maintainable libraries.
分析并重构Dart代码,以遵循惯用的API设计、严格的类型安全和健壮的面向对象层级结构。应用Dart 3的类修饰符来控制扩展和实现边界,通过getter和setter确保适当的封装,并标准化命名约定,以打造可预测、可维护的库。

Instructions

操作指南

  1. Apply Naming Conventions
    • Use noun phrases for non-boolean properties/variables (e.g.,
      list.length
      ).
    • Use non-imperative verb phrases for boolean properties (e.g.,
      isEmpty
      ,
      canClose
      ).
    • Use imperative verbs for methods with side effects (e.g.,
      list.add()
      ).
    • Use
      to___()
      for methods that copy state to a new object (e.g.,
      list.toSet()
      ).
    • Use
      as___()
      for methods returning a different representation backed by the original object (e.g.,
      map.asMap()
      ).
    • Follow standard generic type parameter mnemonics:
      E
      (elements),
      K
      /
      V
      (key/value),
      R
      (return type),
      T
      /
      S
      /
      U
      (general).
  2. Determine Class Modifiers (Decision Logic) Evaluate every class declaration against the intended API boundary.
    • Does the class require a full, concrete implementation of its interface?
      • No: Use
        abstract class
        .
    • Should external libraries be allowed to implement the class interface, but NOT inherit its implementation?
      • Yes: Use
        interface class
        (or
        abstract interface class
        for pure interfaces).
    • Should external libraries be allowed to inherit the implementation, but NOT implement the interface?
      • Yes: Use
        base class
        . (Note: Subclasses must be marked
        base
        ,
        final
        , or
        sealed
        ).
    • Is the class part of a closed, enumerable set of subtypes for exhaustive switching?
      • Yes: Use
        sealed class
        .
    • Should the class be completely locked from external extension and implementation?
      • Yes: Use
        final class
        .
    • Does the class contain only a single abstract method (e.g.,
      call
      )?
      • Yes: Convert to a
        typedef
        function type.
    • Does the class contain only static members?
      • Yes: Move members to top-level library declarations.
    • STOP AND ASK THE USER: If the extension/implementation intent of a public class is ambiguous, pause and ask the user to clarify the intended API boundary before applying modifiers.
  3. Enforce Parameter Rules
    • Prefer named parameters for functions with more than two arguments.
    • Avoid positional boolean parameters.
    • Omit the verb (
      is
      ,
      can
      ,
      has
      ) for named boolean parameters.
    • Use inclusive start and exclusive end parameters for ranges.
    dart
    // GOOD
    void configure(String name, {bool paused = false, bool growable = true}) {}
    var sub = items.sublist(0, 3); 
    
    // BAD
    void configure(String name, bool isPaused, bool canGrow) {}
  4. Manage State and Encapsulation
    • Prefer
      final
      for fields and top-level variables.
    • Avoid public fields; use getters and setters for encapsulation.
    • Do not define a setter without a corresponding getter.
    • Avoid public
      late final
      fields without initializers (this implicitly exposes a setter).
    • Use getters for operations that conceptually access properties (no arguments, returns a result, idempotent, no user-visible side effects).
    dart
    // GOOD
    class Rectangle {
      double _width;
      Rectangle(this._width);
      
      double get width => _width;
      set width(double value) => _width = value;
      
      double get area => _width * _width; // Idempotent, no side effects
    }
  5. Apply Strict Type Annotations
    • Annotate variables without initializers.
    • Annotate fields and top-level variables if the type isn't obvious.
    • Annotate return types and parameter types on non-local function declarations.
    • Do NOT redundantly annotate initialized local variables.
    • Do NOT annotate inferred parameter types on function expressions (closures).
    • Do NOT type annotate initializing formals (
      this.x
      ).
    • Use inline function types over legacy typedefs.
    • Use
      Future<void>
      (not
      void
      or
      Future<Null>
      ) for async members that do not produce values.
    • Avoid
      FutureOr<T>
      as a return type; return
      Future<T>
      instead.
    dart
    // GOOD
    typedef Comparison<T> = int Function(T a, T b);
    
    class FilteredObservable {
      final bool Function(Event) _predicate; // Inline function type
      FilteredObservable(this._predicate);
    }
  6. Implement Equality Safely
    • If overriding
      ==
      , you MUST override
      hashCode
      .
    • Ensure
      ==
      is reflexive, symmetric, and transitive.
    • Avoid defining custom equality for mutable classes.
    • Do not make the parameter to
      ==
      nullable (
      Object
      instead of
      Object?
      ).
    dart
    // GOOD
    class Person {
      final String name;
      Person(this.name);
    
      
      bool operator ==(Object other) => other is Person && name == other.name;
    
      
      int get hashCode => name.hashCode;
    }
  1. 应用命名约定
    • 非布尔类型的属性/变量使用名词短语(例如:
      list.length
      )。
    • 布尔类型的属性使用非命令式动词短语(例如:
      isEmpty
      canClose
      )。
    • 有副作用的方法使用命令式动词(例如:
      list.add()
      )。
    • 用于将状态复制到新对象的方法使用
      to___()
      格式(例如:
      list.toSet()
      )。
    • 用于返回由原对象支持的不同表示形式的方法使用
      as___()
      格式(例如:
      map.asMap()
      )。
    • 遵循标准泛型类型参数助记符:
      E
      (元素)、
      K
      /
      V
      (键/值)、
      R
      (返回类型)、
      T
      /
      S
      /
      U
      (通用类型)。
  2. 确定类修饰符(决策逻辑) 根据预期的API边界评估每个类声明。
    • 该类是否需要其接口的完整具体实现?
      • 否: 使用
        abstract class
    • 是否允许外部库实现该类接口,但不继承其实现?
      • 是: 使用
        interface class
        (纯接口使用
        abstract interface class
        )。
    • 是否允许外部库继承其实现,但不实现该接口?
      • 是: 使用
        base class
        。(注意:子类必须标记为
        base
        final
        sealed
        )。
    • 该类是否属于可穷举切换的封闭可枚举子类型集合?
      • 是: 使用
        sealed class
    • 是否要完全禁止外部扩展和实现该类?
      • 是: 使用
        final class
    • 该类是否仅包含单个抽象方法(例如
      call
      )?
      • 是: 转换为
        typedef
        函数类型。
    • 该类是否仅包含静态成员?
      • 是: 将成员移至顶级库声明中。
    • 暂停并询问用户: 如果公共类的扩展/实现意图不明确,请暂停并询问用户以明确预期的API边界,再应用修饰符。
  3. 实施参数规则
    • 参数超过两个的函数优先使用命名参数。
    • 避免使用位置布尔参数。
    • 命名布尔参数省略动词(
      is
      can
      has
      )。
    • 范围参数使用包含起始值和排除结束值的方式。
    dart
    // GOOD
    void configure(String name, {bool paused = false, bool growable = true}) {}
    var sub = items.sublist(0, 3); 
    
    // BAD
    void configure(String name, bool isPaused, bool canGrow) {}
  4. 管理状态与封装
    • 字段和顶级变量优先使用
      final
    • 避免使用公共字段;使用getter和setter实现封装。
    • 不要定义没有对应getter的setter。
    • 避免使用未初始化的公共
      late final
      字段(这会隐式暴露setter)。
    • 概念上属于访问属性的操作使用getter(无参数、返回结果、幂等、无用户可见副作用)。
    dart
    // GOOD
    class Rectangle {
      double _width;
      Rectangle(this._width);
      
      double get width => _width;
      set width(double value) => _width = value;
      
      double get area => _width * _width; // 幂等,无副作用
    }
  5. 应用严格类型注解
    • 未初始化的变量添加类型注解。
    • 类型不明确的字段和顶级变量添加类型注解。
    • 非局部函数声明添加返回类型和参数类型注解。
    • 已初始化的局部变量不要重复添加类型注解。
    • 函数表达式(闭包)的推断参数类型不要添加注解。
    • 初始化形式参数(
      this.x
      )不要添加类型注解。
    • 使用内联函数类型替代旧版typedef。
    • 不产生值的异步成员使用
      Future<void>
      (而非
      void
      Future<Null>
      )。
    • 避免使用
      FutureOr<T>
      作为返回类型;应返回
      Future<T>
    dart
    // GOOD
    typedef Comparison<T> = int Function(T a, T b);
    
    class FilteredObservable {
      final bool Function(Event) _predicate; // 内联函数类型
      FilteredObservable(this._predicate);
    }
  6. 安全实现相等性
    • 如果重写
      ==
      ,必须同时重写
      hashCode
    • 确保
      ==
      满足自反性、对称性和传递性。
    • 避免为可变类定义自定义相等性。
    • ==
      的参数不要设为可空类型(使用
      Object
      而非
      Object?
      )。
    dart
    // GOOD
    class Person {
      final String name;
      Person(this.name);
    
      
      bool operator ==(Object other) => other is Person && name == other.name;
    
      
      int get hashCode => name.hashCode;
    }

Constraints

约束条件

  • No Positional Booleans: Never generate function signatures with positional boolean arguments.
  • No Legacy Typedefs: Never use the
    typedef name(args)
    syntax. Always use
    typedef Name = ReturnType Function(args)
    .
  • No Faked Overloading: Do not use runtime type tests (
    is
    ) to fake method overloading. Create distinctly named methods instead.
  • No Nullable Collections: Avoid returning nullable
    Future
    ,
    Stream
    , or collection types. Return empty collections instead.
  • No Fluent
    this
    :
    Avoid returning
    this
    from methods just to enable a fluent interface; rely on Dart's cascade operator (
    ..
    ) instead.
  • No Raw Types: Never write incomplete generic types (e.g.,
    List numbers = []
    ). Always specify type arguments or rely on complete inference.
  • Related Skills:
    dart-effective-style
    ,
    dart-package-management
    .
  • 禁止位置布尔参数: 永远不要生成包含位置布尔参数的函数签名。
  • 禁止旧版Typedef: 永远不要使用
    typedef name(args)
    语法。始终使用
    typedef Name = ReturnType Function(args)
  • 禁止模拟重载: 不要使用运行时类型测试(
    is
    )来模拟方法重载。应创建名称明确的方法。
  • 禁止可空集合: 避免返回可空的
    Future
    Stream
    或集合类型。应返回空集合。
  • 禁止Fluent
    this
    不要为了实现流畅接口而从方法返回
    this
    ;应依赖Dart的级联运算符(
    ..
    )。
  • 禁止原始类型: 永远不要编写不完整的泛型类型(例如:
    List numbers = []
    )。始终指定类型参数或依赖完整的类型推断。
  • 相关技能:
    dart-effective-style
    dart-package-management