1.4 代碼風(fēng)格指南
標(biāo)識符
在 Dart 中標(biāo)識符有三種類型
- UpperCamelCase 每個單詞的首字母都大寫,包含第一個單詞
- lowerCamelCase 每個單詞的首字母都大寫,除了第一個單詞, 第一個單詞首字母小寫,即使是縮略詞
- lowercase_with_underscores 只是用小寫字母單詞,即使是縮略詞, 并且單詞之間使用 _ 連接
要使用 UpperCamelCase 風(fēng)格命名類型
Classes(類名)、 enums(枚舉類型)、 typedefs(類型定義)、 以及 type parameters(類型參數(shù))應(yīng)該把每個單詞的首字母都大寫(包含第一個單詞),不使用分隔符
class SliderMenu { ... }
class HttpRequest { ... }
typedef Predicate \= bool Function<T\>(T value);
這里包括使用元數(shù)據(jù)注解的類
class Foo { const Foo(\[arg\]); }
@Foo(anArg) class A { ... }
@Foo() class B { ... }
要在庫,包,文件夾,源文件中使用
lowercase\_with\_underscores 方式命名
Linter rules: library_names, file_names
要用 lowercase_with_underscores 風(fēng)格命名庫和源文件名
一些文件系統(tǒng)不區(qū)分大小寫,所以很多項目要求文件名必須是小寫字母。 使用分隔符這種形式可以保證命名的可讀性。使用下劃線作為分隔符可確保名稱仍然是有效的Dart標(biāo)識符, 如果語言后續(xù)支持符號導(dǎo)入,這將會起到非常大的幫助。
library peg\_parser.source\_scanner;
import 'file\_system.dart';
import 'slider\_menu.dart';
library pegparser.SourceScanner;
import 'file\-system.dart'; import 'SliderMenu.dart';
?? 注意: 如果你選擇命名庫,本準(zhǔn)則給定了如何為庫取名。 如果需要,可以在文件中_省略_庫指令
要使用 lowercase_with_underscores 風(fēng)格命名導(dǎo)入的前綴
Linter rule: library_prefixes
import 'dart:math' as math;
import 'package:angular\_components/angular\_components'
as angular\_components;
import 'package:js/js.dart' as js;
import 'dart:math' as Math;
import 'package:angular\_components/angular\_components'
as angularComponents;
import 'package:js/js.dart' as JS;
要使用 lowerCamelCase 風(fēng)格來命名其他的標(biāo)識符
Linter rule: non_constant_identifier_names
類成員、頂級定義、變量、參數(shù)以及命名參數(shù)等 除了第一個單詞,每個單詞首字母都應(yīng)大寫,并且不使用分隔符。
var item;
HttpRequest httpRequest;
void align(bool clearItems) {
// ...
}
推薦使用 lowerCamelCase 來命名常量
Linter rule: constant_identifier_names
在新的代碼中,使用 lowerCamelCase 來命名常量,包括枚舉的值。 已有的代碼使用了 SCREAMING_CAPS 風(fēng)格, 你可以繼續(xù)全部使用該風(fēng)格來保持代碼的一致性
const pi \= 3.14;
const defaultTimeout \= 1000;
final urlScheme \= RegExp('^(\[a\-z\]+):');
class Dice { static final numberGenerator \= Random(); }
const PI = 3.14;
const DefaultTimeout = 1000;
final URL\_SCHEME = RegExp('^(\[a\-z\]+):');
class Dice { static final NUMBER\_GENERATOR = Random(); }
您可以使用 SCREAMING_CAPS 與現(xiàn)有代碼保持一致,比如:
- 將代碼添加到已使用 SCREAMING_CAPS 的文件或庫時。
- 生成與 Java 代碼并行的 Dart 代碼時。例如,來自 protobufs 的枚舉類型
注意: 我們一開始使用 Java SCREAMING_CAPS 風(fēng)格來命名常量。 我們之所以不再使用,是因為:
- SCREAMING_CAPS 很多情況下看起來比較糟糕, 尤其類似于 CSS 顏色這類的枚舉值
- 常量常常被修改為 final 類型的非常量變量, 這種情況你還需要修改變量的名字為小寫字母形式
- 在枚舉類型中自動定義的 values 屬性為常量并且是小寫字母 形式的
要把超過兩個字母的首字母大寫縮略詞和縮寫詞當(dāng)做一般單詞來對待
首字母大寫縮略詞比較難閱讀, 特別是多個縮略詞連載一起的時候會引起歧義。 例如,一個以 HTTPSFTP 開頭的名字, 沒有辦法判斷它是指 HTTPS FTP 還是 HTTP SFTP 。
為了避免上面的情況,縮略詞和縮寫詞要像普通單詞一樣首字母大寫, 兩個字母的單詞除外。 (像 ID 和 Mr. 這樣的雙字母縮寫詞仍然像一般單詞一樣首字母大寫。)
HttpConnectionInfo uiHandler IOStream HttpRequest Id DB
HTTPConnection UiHandler IoStream HTTPRequest ID Db
譯者注:
- acronyms:首字母縮略詞,指取若干單詞首字母組成一個新單詞,如:HTTP = HyperText Transfer Protocol
- abbreviations: 縮寫詞,指取某一單詞的部分字母(或其他縮短單詞的方式)代表整個單詞,如:ID = identification
不要 使用前綴字母
在編譯器無法幫助你了解自己代碼的時, 匈牙利命名法 和其他方案出現(xiàn)在了 BCPL , 但是因為 Dart 可以提示你聲明的類型,范圍,可變性和其他屬性, 所以沒有理由在標(biāo)識符名稱中對這些屬性進(jìn)行編碼。
defaultTimeout ??
kDefaultTimeout ?
順序
為了使文件前面部分保持整潔,我們規(guī)定了關(guān)鍵字出現(xiàn)順序的規(guī)則。 每個“部分”應(yīng)該使用空行分割。
A single linter rule handles all the ordering guidelines: directives_ordering
要 把 “dart:” 導(dǎo)入語句放到其他導(dǎo)入語句之前
Linter rule: directives_ordering
import 'dart:async';
import 'dart:html';
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';
要把 “package:” 導(dǎo)入語句放到項目相關(guān)導(dǎo)入語句之前
Linter rule: directives_ordering
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';
import 'util.dart';
推薦把外部擴展 “package:” 導(dǎo)入語句放到其他語句之前
如果你使用了多個 “package:” 導(dǎo)入語句來導(dǎo)入自己的包以及其他外部擴展包, 推薦將自己的包分開放到一個額外的部分
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';
import 'package:my\_package/util.dart';
要把導(dǎo)出(export)語句作為一個單獨的部分放到所有導(dǎo)入語句之后
Linter rule: directives_ordering
import 'src/error.dart';
import 'src/foo\_bar.dart';
export 'src/error.dart';
import 'src/error.dart';
export 'src/error.dart';
import 'src/foo\_bar.dart';
要 按照字母順序package:my_package/util
Linter rule: directives_ordering
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';
import 'foo.dart';
import 'foo/foo.dart';
import 'package:foo/foo.dart';
import 'package:bar/bar.dart';
import 'foo/foo.dart';
import 'foo.dart';
格式化
和其他大部分語言一樣, Dart 忽略空格。但是,人不會。 具有一致的空格風(fēng)格有助于幫助我們能夠用編譯器相同的方式理解代碼
要使用 dartfmt 格式化你的代碼
格式化是一項繁瑣的工作,尤其在重構(gòu)過程中特別耗時。 慶幸的是,你不必?fù)?dān)心。 我們提供了一個名為 dartfmt 的優(yōu)秀的自動代碼格式化程序,它可以為你完成格式化工作。 我們有一些關(guān)于它適用的規(guī)則的 文檔 , Dart 中任何官方的空格處理規(guī)則由 dartfmt 生成
其余格式指南用于 dartfmt 無法修復(fù)的一些規(guī)則
考慮修改你的代碼讓格式更友好
無論你扔給格式化程序什么樣代碼,它都會盡力去處理, 但是格式化程序不會創(chuàng)造奇跡。 如果代碼里有特別長的標(biāo)識符,深層嵌套的表達(dá)式,混合的不同類型運算符等。 格式化輸出的代碼可能任然很難閱讀。
當(dāng)有這樣的情況發(fā)生時,那么就需要重新組織或簡化你的代碼。 考慮縮短局部變量名或者將表達(dá)式抽取為一個新的局部變量。 換句話說,你應(yīng)該做一些手動格式化并增加代碼的可讀性的修改。 在工作中應(yīng)該把 dartfmt 看做一個合作伙伴, 在代碼的編寫和迭代過程中互相協(xié)作輸出優(yōu)質(zhì)的代碼
避免單行超過 80 個字符
Linter rule: lines_longer_than_80_chars
可讀性研究表明,長行的文字不易閱讀, 長行文字移動到下一行的開頭時,眼睛需要移動更長的距離。 這也是為什么報紙和雜志會使用多列樣式的文字排版。
如果你真的發(fā)現(xiàn)你需要的文字長度超過了 80 個字符, 根據(jù)我們的經(jīng)驗,你的代碼很可能過于冗長, 而且有方式可以讓它更緊湊。 最常見的的一種情況就是使用 VeryLongCamelCaseClassNames (非常長的類名字和變量名字)。 當(dāng)遇到這種情況時,請自問一下:“那個類型名稱中的每個單詞都會告訴我一些關(guān)鍵的內(nèi)容或阻止名稱沖突嗎?”, 如果不是,考慮刪除它。
注意,dartfmt 能自動處理 99% 的情況,但是剩下的 1% 需要你自己處理。 dartfmt 不會把很長的字符串字面量分割為 80 個字符的列, 所以這種情況你需要自己手工確保每行不超過 80 個字符。
對于包含 URIs 的字符串則是一個例外—主要是導(dǎo)入和導(dǎo)出語句。 如果導(dǎo)入導(dǎo)出語句很長,則還是放到同一行上。 這樣可以方便搜索某一個路徑下的代碼文件。
我們對 URI 和文件路徑做了例外。 當(dāng)情況出現(xiàn)在注釋或字符串是(通常在導(dǎo)入和導(dǎo)出語句中), 即使文字超出行限制,也可能會保留在一行中。 這樣可以更輕松地搜索給定路徑的源文件
要對所有流控制結(jié)構(gòu)使用花括號
Linter rule: curly_braces_in_flow_control_structures
這樣可以避免 dangling else (else懸掛)的問題
if (isWeekDay) {
print('Bike to work!');
} else {
print('Go dancing or read a book!');
}
這里有一個例外:一個沒有 else 的 if 語句, 并且這個 if 語句以及它的執(zhí)行體適合在一行中實現(xiàn)。 在這種情況下,如果您愿意,可以不用括號:
if (arg \== null) return defaultValue;
但是,如果執(zhí)行體包含下一行,請使用大括號:
if (overflowChars != other.overflowChars) {
return overflowChars < other.overflowChars;
}
if (overflowChars != other.overflowChars)
return overflowChars < other.overflowChars;