Dart 代码注释和文档编写规范

7,799 阅读7分钟

「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战

前言

好的文档是代码编写成功的关键要素 —— 虽然在快速开发的当今,我们不爱写文档。但是实际上代码文档能够帮我们理顺思路,定义好输入输出参数,同时也能够帮助我们日后回顾代码,为团队其他人或是代码接手人提供第一手参考资料。比如,Flutter 的各个组件的注释就非常好,事实上,那些注释直接生成了 Flutter 的组件使用说明文档。 当然,也不能为了写文档而写文档,代码应该本身就是一个好的文档,我们应该只在需要的地方写文档,而不是随意地写一些没有意义的内容。本篇来介绍 Dart 官方推荐的代码文档编写规范。

注释

在 Dart 代码中,注释是指那些不需要包括在自动生成文档里的对代码的补充说明。

注释规范1:使用像句子那样的注释

使用句子能够保持注释意思的上下文的完整性,而像短语、缩写这类的注释可能每个人的理解都不同。

// 如果列表元素为空,则直接返回 false。
if (_itemList.isEmpty) return false;

注释规范2:不要使用块注释来当做文档

// 正确示例
void greet(String name) {
  // 假设我们有一个有效的名字。
  print('Hi, $name');
}

// 错误示例
void greet(String name) {
  /* 假设我们有一个有效的名字。 */
  print('Hi, $name!');
}

可以在一个代码块外面使用/* ... */块注释,但代码其他地方应该使用 // 注释。

文档注释

由于可以通过 dartdoc 解析文档注释并生成漂亮的文档页面,使得文档注释特别方便。

文档注释规范1:使用 /// 编写文档注释

在 Dart 中,与其他众多语言不同的是,Dart 使用的是三个斜杠/// 来作为文档注释 —— 这其实是为了更方便地编写注释,相比键盘上打出 /** */这样的 Java 文档风格的文档注释来说,使用/// 显然更加快捷(无需按 shift 键切换)。同时,对于像 Markdown 这样的文档来说,* 其实是一个圆点列表的标识。

// 正确示例

/// 获取未分割前块中的字符数
int get length => ...
  
// 错误示例
  
// 获取未分割前块中的字符数
int get length => ...

文档注释规范2:对于公共 API 应当编写文档

公共 API 是对外开放的,编写文档有助于生成对外的 API 文档,从而使得调用者更清晰地了解这些 API 的功能、输入输出参数。

文档注释规范3:编写库级别的文档注释

Dart 语言中,一个库的注释文档是使用者了解该库的最佳方式,编写库的文档建议按如下方式进行:

  • 一个简短的总结告知使用者这个库的用途。
  • 介绍库中通篇用到的术语。
  • 一组完整的使用该库 API 的示例代码。
  • 链接相关重要或通用的类或方法。
  • 为库中涉及到的外部引用提供访问链接。

例如,下面是从 Flutter 中摘抄 Animation<T> 类的一段注释。

/// An animation with a value of type `T`.
///
/// An animation consists of a value (of type `T`) together with a status. The
/// status indicates whether the animation is conceptually running from
/// beginning to end or from the end back to the beginning, although the actual
/// value of the animation might not change monotonically (e.g., if the
/// animation uses a curve that bounces).
///
/// Animations also let other objects listen for changes to either their value
/// or their status. These callbacks are called during the "animation" phase of
/// the pipeline, just prior to rebuilding widgets.
///
/// To create a new animation that you can run forward and backward, consider
/// using [AnimationController].
///
/// See also:
///
///  * [Tween], which can be used to create [Animation] subclasses that
///    convert `Animation<double>`s into other kinds of `Animation`s.

文档注释规范4:为私有 API 编写文档

私有 API 虽然不对外开放,但是对于内部调用私有成员的理解会更有帮助,有助于我们梳理内部的 API 代码。

文档注释规范5:保持合理的注释段落结构

通常,应当在某个代码块的第一句表名该代码块的用途,然后在另起一个段落来做进一步的描述。这样会让阅读代码的人第一眼就能了解代码块的功能。

// 正确示例1

/// Deletes the file at [path] from the file system.
void delete(String path) {
  ...
}

// 正确示例2

/// Deletes the file at [path].
///
/// Throws an [IOError] if the file could not be found. Throws a
/// [PermissionError] if the file is present but could not be deleted.
void delete(String path) {
  ...
}

// 错误示例

/// Depending on the state of the file system and the user's permissions,
/// certain operations may or may not be possible. If there is no file at
/// [path] or it can't be accessed, this function throws either [IOError]
/// or [PermissionError], respectively. Otherwise, this deletes the file.
void delete(String path) {
  ...
}

文档注释规范6:上下文清晰的情况下不要重复介绍

像下面的错误示例中的 radio button 就很多余,本身组件就已经指明了是 RadioButtonWidget 了。

// 正确示例

class RadioButtonWidget extends Widget {
  /// Sets the tooltip to [lines], which should have been word wrapped using
  /// the current font.
  void tooltip(List<String> lines) {
    ...
  }
}

// 错误示例

class RadioButtonWidget extends Widget {
  /// Sets the tooltip for this radio button widget to the list of strings in
  /// [lines].
  void tooltip(List<String> lines) {
    ...
  }
}

如果你想不出要对代码做什么注释的话,那么宁愿空着也不要写无谓的注释,那样只会耽误阅读者的时间。

文档注释规范7:在文档中提供示例代码

有 demo 的话,调用者会更快速地理解代码,能够节省很多时间,大幅度提高开发效率。

/// Returns the lesser of two numbers.
///
/// ```dart
/// min(5, 3) == 3
/// ```
num min(num a, num b) => ...

文档注释规范8:使用方括号包裹文件内的标识符

如果使用了方括号将变量、方法、类型名包裹起来后,dartdoc 会会查找相同的名称,并将这些名称链接到文档中。父级属性是可选的,但是提供的话会更有利于理解。

/// Throws a [StateError] if ...
/// similar to [anotherMethod()], but ...

/// Similar to [Duration.inDays], but handles fractional days.

/// To create a point from polar coordinates, use [Point.polar()].

文档注释规范9:将需要解释的参数、返回值和异常通过语言组织起来

很多编程语言会使用冗长的标签和区块来描述返回值和参数,但 Dart 不推荐这么做,而是推荐使用自然语言的方式组织这些元素,使得阅读更为顺畅。

// 错误示例

/// Defines a flag with the given name and abbreviation.
///
/// @param name The name of the flag.
/// @param abbr The abbreviation for the flag.
/// @returns The new flag.
/// @throws ArgumentError If there is already an option with
///     the given name or abbreviation.
Flag addFlag(String name, String abbr) => ...
  
// 正确示例
  
/// Defines a flag.
///
/// Throws an [ArgumentError] if there is already an option named [name] or
/// there is already an option using abbreviation [abbr]. Returns the new flag.
Flag addFlag(String name, String abbr) => ...

文档注释规范10:将文档注释放在元数据的注解前

// 正确示例

/// A button that can be flipped on and off.
@Component(selector: 'toggle')
class ToggleComponent {}

// 错误示例

@Component(selector: 'toggle')
/// A button that can be flipped on and off.
class ToggleComponent {}

Markdown

在 Dart 的注释文档中可以使用 Markdown 语法。因此为了阅读体验,可以使用 Markdown 语法让文档更有层次,更易于阅读。但是,请不要炫技,过度使用 Markdown 语法。同时,对于代码块,推荐使用 dart ... 块代码形式,这样形成的代码可读性会更强。

更多内容

本篇介绍了 Dart 语言的代码文档和注释规范,实际上官网都是英文示例,有些内容可能不太适用中文。但是,假设要编写开源的插件库的话,那么肯定是需要使用英文,这时候使用官方的文档注释风格将会使得你的插件更受欢迎,更多内容可以参考官网的指引:Dart 文档注释指引。

我是岛上码农,微信公众号同名,这是Flutter 入门与实战的专栏文章,提供体系化的 Flutter 学习文章。对应源码请看这里:Flutter 入门与实战专栏源码。如有问题可以加本人微信交流,微信号:island-coder

👍🏻:觉得有收获请点个赞鼓励一下!

🌟:收藏文章,方便回看哦!

💬:评论交流,互相进步!