本篇简要的说明一下Dart在编写过程中官方推荐的一些实践,有以下几个方面:
-
代码风格 -
文档 -
应用示例 -
API设计
代码风格
在 Dart 中标识符有三种类型。
-
UpperCamelCase 每个单词的首字母都大写,包含第一个单词。 -
lowerCamelCase 除了第一个字母始终是小写(即使是缩略词),每个单词的首字母都大写。 -
lowercase_with_underscores 只是用小写字母单词,即使是缩略词,并且单词之间使用 _ 连接。
即SliderMenu
、clearItems
和angular_components
。
UpperCamelCase 风格命名类型
Classes(类名)、 enums(枚举类型)、 typedefs(类型定义)、以及 type parameters(类型参数)应该把每个单词的首字母都大写(包含第一个单词),不使用分隔符。
class SliderMenu { ... }
class HttpRequest { ... }
typedef Predicate<T> = bool Function(T value);
使用 UpperCamelCase 风格类型作为扩展名
与类型命名一样,扩展 的名称也应大写每个单词的首字母(包括第一个单词),并且不使用分隔符。
extension MyFancyList<T> on List<T> { ... }
extension SmartIterable<T> on Iterable<T> { ... }
在库,package,文件夹,源文件 中使用 lowercase_with_underscores 方式命名
library peg_parser.source_scanner;
import 'file_system.dart';
import 'slider_menu.dart';
用 lowercase_with_underscores 风格命名库和源文件名
import 'dart:math' as math;
import 'package:angular_components/angular_components.dart' as angular_components;
import 'package:js/js.dart' as js;
使用 lowerCamelCase 风格来命名其他的标识符
var count = 3;
HttpRequest httpRequest;
void align(bool clearItems) {
// ...
}
使用 lowerCamelCase 来命名常量
const pi = 3.14;
const defaultTimeout = 1000;
final urlScheme = RegExp('^([a-z]+):');
class Dice {
static final numberGenerator = Random();
}
使用 _ 替代用不到的回调值
futureOfVoid.then((_) {
print('Operation complete.');
});
把 “dart:” 导入语句放到其他导入语句之前
import 'dart:async';
import 'dart:html';
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';
要 把 “package:” 导入语句放到项目相关导入语句之前
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';
import 'util.dart';
把导出(export)语句作为一个单独的部分放到所有导入语句之后
import 'src/error.dart';
import 'src/foo_bar.dart';
export 'src/error.dart';
按照字母顺序来排序每个部分中的语句
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';
import 'foo.dart';
import 'foo/foo.dart';
文档
使用 /// 文档注释来注释成员和类型
/// The number of characters in this chunk when unsplit.
int get length => ...
要在文档注释开头有一个单句总结
/// Deletes the file at [path] from the file system.
void delete(String path) {
...
}
让文档注释的第一句从段落中分开
在第一句之后添加一个空行,将其拆分为自己的段落。如果不止一个解释句子有用,请将其余部分放在后面的段落中。
这有助于您编写一个紧凑的第一句话来总结文档。此外,像Dartdoc这样的工具使用第一段作为类和类成员列表等地方的简短摘要。
/// 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) {
...
}
避免 与周围上下文冗余。
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) {
...
}
}
用第三人称来开始函数或者方法的文档注释
/// Returns `true` if every element satisfies the [predicate].
bool all(bool predicate(T element)) => ...
/// Starts the stopwatch if not already running.
void start() {
...
}
使用名词短语来为非布尔值变量或属性注释。
/// The current day of the week, where `0` is Sunday.
int weekday;
/// The number of checked buttons on the page.
int get checkedCount => ...
使用名词短语来开始库和类型注释
在程序中,类的注释通常是最重要的文档。类的注释描述了类型的不变性、介绍其使用的术语、提供类成员使用的上下文信息。为类提供一些注释可以让其他类成员的注释更易于理解和编写。
/// A chunk of non-breaking output text terminated by a hard or soft newline.
///
/// ...
class Chunk { ... }
在文档注释中添加示例代码。
/// Returns the lesser of two numbers.
///
/// ```dart
/// min(5, 3) == 3
/// ```
num min(num a, num b) => ...
人类非常擅长从示例中抽象出实质内容,所以即使提供一行最简单的示例代码都可以让 API 更易于理解。
使用方括号在文档注释中引用作用域内的标识符
如果给变量,方法,或类型等名称加上方括号,则 dartdoc 会查找名称并链接到相关的 API 文档。括号是可选的,但是当你在引用一个方法或者构造函数时,可以让注释更清晰。
/// Throws a [StateError] if ...
/// similar to [anotherMethod()], but ...
使用散文的方式来描述参数、返回值以及异常信息
Dart不像其他语言,它推荐用一段话描述参数的内容:
/// 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) => ...
而不是:
/// 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) => ...
用法示例
在 part of 中使用字符串
很多 Dart 开发者会避免直接使用 part 。他们发现当库仅有一个文件的时候很容易读懂代码。如果你确实要使用 part 将库的一部分拆分为另一个文件,则 Dart 要求另一个文件指示它所属库的路径。由于遗留原因, Dart 允许 part of 指令使用它所属的库的名称。这使得工具很难直接查找到这个文件对应主库文件,使得库和文件之间的关系模糊不清。
推荐的现代语法是使用 URI 字符串直接指向库文件。首选的现代语法是使用直接指向库文件的URI字符串,URI 的使用和其他指令中一样。如果你有一些库,my_library.dart,其中包含:
library my_library;
part 'some/other/file.dart';
从库中拆分的文件应该如下所示:
part of '../../my_library.dart';
而不是:
part of my_library;
不要 导入 package 中 src 目录下的库
lib 下的 src 目录 被指定 为 package 自己实现的私有库。基于包维护者对版本的考虑,package 使用了这种约定。在不破坏 package 的情况下,维护者可以自由地对 src 目录下的代码进行修改。
这意味着,你如果导入了其中的私有库,按理论来讲,一个不破坏 package 的次版本就会影响到你的代码。
推荐使用相对路径引入
比如,下面是你的 package 目录结构:
my_package
└─ lib
├─ src
│ └─ stuff.dart
│ └─ utils.dart
└─ api.dart
test
│─ api_test.dart
└─ test_utils.dart
Here is how the various libraries should import each other:
如果 api.dart 想导入 utils.dart ,应该这样使用:
import 'src/stuff.dart';
import 'src/utils.dart';
// lib/src/utils.dart:
import '../api.dart';
import 'stuff.dart';
// test/api_test.dart:
import 'package:my_package/api.dart'; // Don't reach into 'lib'.
import 'test_utils.dart'; // Relative within 'test' is fine.
使用相邻字符串的方式连接字面量字符串
如果你有两个字面量字符串(不是变量,是放在引号中的字符串),你不需要使用 + 来连接它们。应该像 C 和 C++ 一样,只需要将它们挨着在一起就可以了。这种方式非常适合不能放到一行的长字符串的创建。
raiseAlarm('ERROR: Parts of the spaceship are on fire. Other '
'parts are overrun by martians. Unclear which are which.');
使用插值的形式来组合字符串和值
如果你之前使用过其他语言,你一定习惯使用大量 + 将字面量字符串以及字符串变量链接构建字符串。这种方式在 Dart 中同样有效,但是通常情况下使用插值会更清晰简短:
'Hello, $name! You are ${year - birth} years old.';
避免 在字符串插值中使用不必要的大括号
如果要插入是一个简单的标识符,并且后面没有紧跟随在其他字母文本,则应省略 {} 。
var greeting = 'Hi, $name! I love your ${decade}s costume.';
要 尽可能的使用集合字面量
Dart 有三种核心集合类型。List、Map 和 Set,这些类和大多数类一样,都有未命名的构造函数,但由于这些集合使用频率很高,Dart 有更好的内置语法来创建它们:
var points = <Point>[];
var addresses = <String, Address>{};
var counts = <int>{};
而不是:
var addresses = Map<String, Address>();
var counts = Set<int>();
不要 使用 .length 来判断一个集合是否为空
Iterable 合约并不要求集合知道其长度,也没要求在遍历的时候其长度不能改变。通过调用 .length 来判断集合是否包含内容是非常低效的。
相反,Dart 提供了更加高效率和易用的 getter 函数:.isEmpty 和.isNotEmpty。使用这些函数并不需要对结果再次取非。
if (lunchBox.isEmpty) return 'so hungry...';
if (words.isNotEmpty) return words.join(' ');
避免 在 Iterable.forEach() 中使用字面量函数
forEach() 函数在 JavaScript 中被广泛使用,这因为内置的 for-in 循环通常不能达到你想要的效果。在Dart中,如果要对序列进行迭代,惯用的方式是使用循环。
for (final person in people) {
...
}
而不是
people.forEach((person) {
...
});
API 设计
要 使用一致的术语
在你的代码中,同样的东西要使用同样的名字。如果之前已经存在的 API 之外命名,并且用户已经熟知,那么请继续使用这个命名。
pageCount // A field.
updatePageCount() // Consistent with pageCount.
toSomething() // Consistent with Iterable's toList().
asSomething() // Consistent with List's asMap().
Point // A familiar concept.
总的目的是充分利用用户已经知道的内容。这里包括他们所了解的问题领域,所熟悉的核心库,以及你自己 API 那部分。基于以上这些内容,他们在使用之前,不需要学习大量的新知识。
避免 缩写
pageCount
buildRectangles
IOStream
HttpRequest
推荐 把最具描述性的名词放到最后
pageCount // A count (of pages).
ConversionSink // A sink for doing conversions.
ChunkedConversionSink // A ConversionSink that's chunked.
CssFontFaceRule // A rule for font faces in CSS.
考虑 尽量让代码看起来像普通的句子
// "If errors is empty..."
if (errors.isEmpty) ...
// "Hey, subscription, cancel!"
subscription.cancel();
// "Get the monsters where the monster has claws."
monsters.where((monster) => monster.hasClaws);
推荐 使用名词短语来命名不是布尔类型的变量和属性
读者关注属性是什么。如果用户更关心如何确定一个属性,则很可能应该是一个使用动词短语命名函数。
list.length
context.lineWidth
quest.rampagingSwampBeast
暂时就写这么多,具体可以到官网上看呢。
发表评论