在过去的几年里,我们编写了大量的 Dart 代码,并从中收获了很多经验和教训。我们将与你分享这些经验,这些经验将有助于你编写出一致、健壮、高效的代码。这里包含两主题:
-
保持一致 当谈论到格式、命名以及参数的相关规则时,哪种规则更好,得出的结论通常是主观且无法达成一致的。但我们知道的是,客观上保持 一致 是非常有益的。
如果两段代码看起来不同,那它们就应该有不同的含义。当一段突出的代码吸引到你的注意时,那它就应该有吸引你的理由。
-
保持简洁 Dart 会让开发者感到很亲切,因为它继承了许多与 C、Java、JavaScript 及其他语言相同的语句和表达式语法。我们之所以开发 Dart 语言,是因为这些语言依然有很大的改进的空间。我们提供了很多新的特性,比如字符串插值、初始化范式等,以帮助你更简单、更轻松地表达意图。
如果有多种方式来描述一件事情,那么你通常应该选择其中最简洁的方式。这并不意味着你要像 code golf(代码高尔夫挑战赛)一样将所有代码塞到一行中。而是应该让代码变得 简约 而非 密集。
指南
为了便于理解,这里我们将指南分成了几个部分:
-
风格指南 – 该部分定义了布局和组织代码的规则,或者说是 dart format 不能为你格式化的那些代码的布局和组织规则。风格指南还为你指定了标识符的格式,比如:驼峰式大小写、下划线的使用等等。
-
注释指南 – 该部分会告诉你注释中应该包含哪些内容。包括文档注释以及常规的普通代码注释。
-
使用指南 – 该部分将教你如何充分利用语言特性来实现相关功能。比如你可以在该部分找到如何利用语句或表达式来实现某个功能。
-
设计指南 – 该部分是最易理解但是覆盖范围最广的。其涵盖了我们所总结的为库设计一致、可用 API 的经验。比如这些 API 的类型签名或声明的说明则会在这里找到。
有关所有指南的链接,请查阅 概览。
如何阅读这些指南
每个指南都分为了几个部分。每个部分包含一些详细的准则。每条准则都以下面其中的一个词作为开头:
-
要 准则所描述的内容应该始终被遵守。不应该以任何的理由来偏离违背这些准则。
-
不要 准则所描述的内容是相反的:描述的准则不是一个好主意。幸运的是,我们不会像其他语言有那么多这样的准则,因为我们没有太多的历史包袱。
-
推荐 准则所描述的内容 应该 被遵守。但是在有些情况下,可能有更好的或者更合理的做法。请确保在你完全理解准则的情况下,再忽视这些准则。
-
避免 该单词描述的准则与 “推荐” 描述的准则相反:显然,这些事不应该做,但不排除在极少数场合下有充分的理由可以做。
-
考虑 准则所描述的内容可以遵守也可以不遵守。取决于具体的情况、习惯做法以及自己的偏好。
某些准则描述了规则 不 适用的 例外情况。当这些例外列出时,也有可能不是详尽的—你可能还需要对其它的情况作出判断。
这听起来好像有点小题大做。其实并没有那么糟糕。大部分的准则都是常识,也符合我们的认知。最终要达到的目标是写出优雅,可读,可维护的代码。
Dart 分析器提供了一个 Linter 工具,可以帮助你编写优秀的、一致性的以及符合各种指南的代码。如果存在一个或者多个 linter 规则 来帮助你遵循某个指南准则,那么这些指南的链接也会有显示。比如下面的示例:
Linter rule: unnecessary_getters_setters
学习如何使用 linter,请查阅文档 启用 linter 规则,以及可用的各种 linter 规则。
术语表
为了使指南保持简洁,我们使用一些简写术语来指代不同的 Dart 结构。
-
库成员 表示一个顶层字段、Getter 或 Setter 方法、或函数。基本而言,任何顶层的东西都不会是一种类型。
-
类成员 表示类内部声明的构造函数、字段、Getter 或 Setter 方法、函数或操作符。类成员可以是实例或静态的,也可以是抽象或具体的。
-
成员 可以表示是一个库成员或者类成员。
-
变量 通常指的是顶层变量、参数和局部变量。它不包括静态或实例字段。
-
类型 表示所有命名类型的声明:一个类、typedef 或枚举。
-
属性 表示顶层变量、Getter 和 Setter 方法(位于类中或顶层,可以是实例或静态的)或字段(实例或静态的)。基本上是任何类似字段的命名结构都可以称为属性。
所有准则摘要
Style
Identifiers
- DO name types using
UpperCamelCase
. - DO name extensions using
UpperCamelCase
. - DO name packages, directories, and source files using
lowercase_with_underscores
. - DO name import prefixes using
lowercase_with_underscores
. - DO name other identifiers using
lowerCamelCase
. - PREFER using
lowerCamelCase
for constant names. - DO capitalize acronyms and abbreviations longer than two letters like words.
- PREFER using
_
,__
, etc. for unused callback parameters. - DON’T use a leading underscore for identifiers that aren’t private.
- DON’T use prefix letters.
- DON’T explicitly name libraries
Ordering
- DO place
dart:
imports before other imports. - DO place
package:
imports before relative imports. - DO specify exports in a separate section after all imports.
- DO sort sections alphabetically.
Formatting
Documentation
Comments
Doc comments
- DO use
///
doc comments to document members and types. - PREFER writing doc comments for public APIs.
- CONSIDER writing a library-level doc comment.
- CONSIDER writing doc comments for private APIs.
- DO start doc comments with a single-sentence summary.
- DO separate the first sentence of a doc comment into its own paragraph.
- AVOID redundancy with the surrounding context.
- PREFER starting function or method comments with third-person verbs.
- PREFER starting a non-boolean variable or property comment with a noun phrase.
- PREFER starting a boolean variable or property comment with “Whether” followed by a noun or gerund phrase.
- DON’T write documentation for both the getter and setter of a property.
- PREFER starting library or type comments with noun phrases.
- CONSIDER including code samples in doc comments.
- DO use square brackets in doc comments to refer to in-scope identifiers.
- DO use prose to explain parameters, return values, and exceptions.
- DO put doc comments before metadata annotations.
Markdown
- AVOID using markdown excessively.
- AVOID using HTML for formatting.
- PREFER backtick fences for code blocks.
Writing
Usage
Libraries
- DO use strings in
part of
directives. - DON’T import libraries that are inside the
src
directory of another package. - DON’T allow an import path to reach into or out of
lib
. - PREFER relative import paths.
Null
- DON’T explicitly initialize variables to
null
. - DON’T use an explicit default value of
null
. - DON’T use
true
orfalse
in equality operations - AVOID
late
variables if you need to check whether they are initialized. - CONSIDER assigning a nullable field to a local variable to enable type promotion.
Strings
- DO use adjacent strings to concatenate string literals.
- PREFER using interpolation to compose strings and values.
- AVOID using curly braces in interpolation when not needed.
Collections
- DO use collection literals when possible.
- DON’T use
.length
to see if a collection is empty. - AVOID using
Iterable.forEach()
with a function literal. - DON’T use
List.from()
unless you intend to change the type of the result. - DO use
whereType()
to filter a collection by type. - DON’T use
cast()
when a nearby operation will do. - AVOID using
cast()
.
Functions
- DO use a function declaration to bind a function to a name.
- DON’T create a lambda when a tear-off will do.
- DO use
=
to separate a named parameter from its default value.
Variables
- DO follow a consistent rule for
var
andfinal
on local variables. - AVOID storing what you can calculate.
Members
- DON’T wrap a field in a getter and setter unnecessarily.
- PREFER using a
final
field to make a read-only property. - CONSIDER using
=>
for simple members. - DON’T use
this.
except to redirect to a named constructor or to avoid shadowing. - DO initialize fields at their declaration when possible.
Constructors
- DO use initializing formals when possible.
- DON’T use
late
when a constructor initializer list will do. - DO use
;
instead of{}
for empty constructor bodies. - DON’T use
new
. - DON’T use
const
redundantly.
Error handling
- AVOID catches without
on
clauses. - DON’T discard errors from catches without
on
clauses. - DO throw objects that implement
Error
only for programmatic errors. - DON’T explicitly catch
Error
or types that implement it. - DO use
rethrow
to rethrow a caught exception.
Asynchrony
Design
Names
- DO use terms consistently.
- AVOID abbreviations.
- PREFER putting the most descriptive noun last.
- CONSIDER making the code read like a sentence.
- PREFER a noun phrase for a non-boolean property or variable.
- PREFER a non-imperative verb phrase for a boolean property or variable.
- CONSIDER omitting the verb for a named boolean parameter.
- PREFER the “positive” name for a boolean property or variable.
- PREFER an imperative verb phrase for a function or method whose main purpose is a side effect.
- PREFER a noun phrase or non-imperative verb phrase for a function or method if returning a value is its primary purpose.
- CONSIDER an imperative verb phrase for a function or method if you want to draw attention to the work it performs.
- AVOID starting a method name with
get
. - PREFER naming a method
to___()
if it copies the object’s state to a new object. - PREFER naming a method
as___()
if it returns a different representation backed by the original object. - AVOID describing the parameters in the function’s or method’s name.
- DO follow existing mnemonic conventions when naming type parameters.
Libraries
Classes and mixins
- AVOID defining a one-member abstract class when a simple function will do.
- AVOID defining a class that contains only static members.
- AVOID extending a class that isn’t intended to be subclassed.
- DO document if your class supports being extended.
- AVOID implementing a class that isn’t intended to be an interface.
- DO document if your class supports being used as an interface.
- DO use
mixin
to define a mixin type. - AVOID mixing in a type that isn’t intended to be a mixin.
Constructors
Members
- PREFER making fields and top-level variables
final
. - DO use getters for operations that conceptually access properties.
- DO use setters for operations that conceptually change properties.
- DON’T define a setter without a corresponding getter.
- AVOID using runtime type tests to fake overloading.
- AVOID public
late final
fields without initializers. - AVOID returning nullable
Future
,Stream
, and collection types. - AVOID returning
this
from methods just to enable a fluent interface.
Types
- DO type annotate variables without initializers.
- DO type annotate fields and top-level variables if the type isn’t obvious.
- DON’T redundantly type annotate initialized local variables.
- DO annotate return types on function declarations.
- DO annotate parameter types on function declarations.
- DON’T annotate inferred parameter types on function expressions.
- DON’T type annotate initializing formals.
- DO write type arguments on generic invocations that aren’t inferred.
- DON’T write type arguments on generic invocations that are inferred.
- AVOID writing incomplete generic types.
- DO annotate with
dynamic
instead of letting inference fail. - PREFER signatures in function type annotations.
- DON’T specify a return type for a setter.
- DON’T use the legacy typedef syntax.
- PREFER inline function types over typedefs.
- PREFER using function type syntax for parameters.
- AVOID using
dynamic
unless you want to disable static checking. - DO use
Future<void>
as the return type of asynchronous members that do not produce values. - AVOID using
FutureOr<T>
as a return type.
Parameters
- AVOID positional boolean parameters.
- AVOID optional positional parameters if the user may want to omit earlier parameters.
- AVOID mandatory parameters that accept a special “no argument” value.
- DO use inclusive start and exclusive end parameters to accept a range.
Equality