Effective Dart: 代碼風格

毋庸置疑好代碼的代碼風格一定是優雅的。一致的命名規則、一致的順序、 以及一致的格式讓代碼看起來是一樣的最終執行就是一樣的。由于人眼識別 系統的工作方式,代碼風格一樣讓我們更容易的閱讀理解代碼。如果在整個 Dart 語 言生態都使用一致的代碼風格,那么所有的 Dart 從業人員都能從中獲取到好處并且 可以很方便的參與到其他人的項目中去。

標識符

Dart 中的標識符有三種風格。

  • UpperCamelCase 風格每個單詞的首字母都大寫,包含第一個單詞。
  • lowerCamelCase 風格每個單詞的首字母大寫,除了第一個單詞,第一個單詞首字母小寫。即使是首字符縮略詞也是這樣。
  • lowercase_with_underscores 風格只使用小寫字母,即使是首字符縮略詞也是這樣,然后使用下劃線 _ 把每個單詞分割開。

使用 UpperCamelCase 風格來命名類型名稱

Classes(類名字)、 enums(枚舉類型)、 typedefs(類型定義)、以及 type parameters(類型參數)應該把每個單詞的首字母都大寫, (包含第一個單詞)并且沒有其他分隔符。

class SliderMenu { ... }

class HttpRequest { ... }

typedef bool Predicate<T>(T value);

用于注解的類也使用該類型命名。

class Foo {
  const Foo([arg]);
}

@Foo(anArg)
class A { ... }

@Foo()
class B { ... }

如果注解類使用的是無參數構造函數,則可以使用一個 lowerCamelCase 風格的常量來初始化這個注解。

const foo = const Foo();

@foo
class C { ... }

使用 lowercase_with_underscores 風格來命名庫和文件名名字。

由于某些文件系統不區分大小寫,所以很多項目都要求文件名必須為小寫字母。 通過分隔符分開單詞可以保證名字的可讀性。使用下劃線作為分隔符確保名字為 合法的 Dart 標識符,如果將來支持符號導入的話 文件名必須要為合法的標識符, 這樣就不用將來再修改文件名了。

這是正確示范:

library peg_parser.source_scanner;

import 'file_system.dart';
import 'slider_menu.dart';

這是錯誤示范:

library pegparser.SourceScanner;

import 'file-system.dart';
import 'SliderMenu.dart';

注意,如果你選擇命名庫的話本指南規定了如何給庫起名字。你也可以選擇忽略 文件中的 library 指令。

使用 lowercase_with_underscores 風格命名導入的前綴。

這是正確示范:

import 'dart:json' as json;
import 'dart:math' as math;
import 'package:javascript_utils/javascript_utils.dart' as js_utils;
import 'package:js/js.dart' as js;

這是錯誤示范:

import 'dart:json' as JSON;
import 'dart:math' as Math;
import 'package:javascript_utils/javascript_utils.dart' as jsUtils;
import 'package:js/js.dart' as JS;

使用 lowerCamelCase 風格來命名其他的標識符。

類成員變量、頂級定義(變量、函數等)、變量、參數以及命名參數等都應該 使用 lowerCamelCase 這種類型的命名風格。 第一個單詞首字母不大寫并且沒有分隔符。

var item;

HttpRequest httpRequest;

align(clearItems) {
  // ...
}

推薦 使用 lowerCamelCase 來命名常量。

在新的代碼中使用 lowerCamelCase 來命名常量,包括枚舉的值。 在已有的代碼中你可以繼續使用 SCREAMING_CAPS 風格來保持 代碼的統一性。

const pi = 3.14;
const defaultTimeout = 1000;
final urlScheme = new RegExp('^([a-z]+):');

class Dice {
  static final numberGenerator = new Random();
}
const PI = 3.14;
const kDefaultTimeout = 1000;
final URL_SCHEME = new RegExp('^([a-z]+):');

class Dice {
  static final NUMBER_GENERATOR = new Random();
}

注意: 一開始我們使用 Java 中的 SCREAMING_CAPS 風格來命名常量。后來我們不再使用 這種風格是由于: SCREAMING_CAPS 在很多場合下看起來很費力,例如用于 CSS 中定義顏色 的枚舉值。 常量常常被修改為 final 類型的非常量變量,這種情況你還需要修改變量的 名字為小寫字母形式。 在枚舉類型中自動定義的 values 屬性為常量并且是小寫字母 形式的。

把超過兩個字母的縮略詞和首字母縮略詞當做一般單詞來對待。

首字母縮略詞都大寫比較難以閱讀,特別是多個首字母縮略詞連在一起的時候。 例如 HTTPSFTPConnection、這種情況下你根本不知道到底是 HTTPS FTP 鏈接還是 HTTP SFTP 鏈接。

為了避免這種情況,把超過兩個字母的縮略詞和首字母縮略詞當做一般單詞來對待。 (兩個字符的 縮寫 要和普通單詞一樣對待,例如 ID 和 Mr. 只首字母大寫,第二個字母小寫。)

這是正確示范:

HttpConnection
uiHandler
IOStream
HttpRequest
Id
DB

這是錯誤示范:

HTTPConnection
UiHandler
IoStream
HTTPRequest
ID
Db

順序

為了保證代碼文件前面部分的整潔,我們規定了每個部分出現的順序。 每個部分之間通過空行來分割。

把 “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 'a.dart';

推薦 把”第三方” “package:” 導入語句放到其他語句之前。

如果你使用了多個 “package:” 導入語句來導入自己項目中的文件和第三方的文件, 推薦把自己的導入語句和其他的導入語句使用空行分開。

import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

import 'package:myapp/io.dart';
import 'package:myapp/util.dart';

把導出(export)語句放到所有導入語句之后的部分。

import 'src/error.dart';
import 'src/string_source.dart';

export 'src/error.dart';
import 'src/error.dart';
export 'src/error.dart';
import 'src/string_source.dart';

按照字母順序來排序每個部分中的語句。

大部分 IDE 都可以自動完成這個操作。

import 'package:bar/bar.dart';
import 'package:foo/bar.dart';

import 'a.dart';
import 'a/b.dart';
import 'package:foo/bar.dart';
import 'package:bar/bar.dart';

import 'a/b.dart';
import 'a.dart';

格式化

和其他大部分語言一樣, Dart 忽略空格。但是,人不這樣。使用一致的 空格風格可以幫助人類按照機器的方式來 理解代碼。

格式化代碼是一個乏味的無聊任務,在重構的時候格式化代碼還會消耗大量時間。 幸運的是,您無需手工來實現這項任務。我們提供了一個頂尖的自動化格式化 工具 – dartfmt 來自動實現這項無聊的任務。 Dart 中官方的空格處理規則由 dartfmt 生成。

避免 每行長度超過 80 字符。

閱讀研究表明長文本眼睛需要費力的追蹤文字換行,比較難以閱讀。 這就是為何報紙紙張這么大,但是每頁紙都分成很多短列來顯示 內容。

如果你確實需要大于 80 個字符的代碼行,我們的經驗表明 您的代碼很有可能太啰嗦了,可以變得更加簡練。 最常見的的一種情況就是使用 VeryLongCamelCaseClassNames(非常長的類名字和變量名字)。 當你遇到這種情況,請自問一下:“是否每個單詞都表達了一些關鍵信息并且防止出現 名字沖突的情況?”,如果回答是否,請考慮刪除一些單詞。

注意,dartfmt 能自動處理 99% 的情況,但是剩下的 1% 取決于您自己。 dartfmt 不會 把很長的字符串字面量分割為 80 個字符的列,所以這種情況你需要自己手工 確保每行不超過 80 個字符。

對于包含 URIs 的字符串則是一個例外—主要是導入和導出語句。 如果導入導出語句很長,則還是放到同一行上。 這樣可以方便搜索某一個路徑下的代碼文件。

在所有的控制結構上使用大括號。

這樣可以避免 dangling else (懸掛else)的問題。

if (true) {
  print('sanity');
} else {
  print('opposite day!');
}

當只有 if 語句沒有 else 語句并且 所有語句可以放到一行的時候,可以省略大括號。

if (arg == null) return defaultValue;

通常用于當條件滿足的時候就跳出 if 或者 返回的情況。 但是對于其他表達式,如果可以放到一行中, 也可以這樣使用。

if (parameter == null) parameter = defaultValue;

使用 dartfmt 來格式化您的代碼

dartfmt 自動使用下面所有的規則格式化代碼,并且 還有其他的一些微妙的情況。自動化比手工要快很多并且還不會出錯。 如果你每次都使用 dartfmt 的話,下面的所有規則都不用閱讀了。

不要 使用 tabs 。

使用空格確保在所有的編輯器下代碼看起來都是一致的。 并且還可以確保把代碼發布到網站、博客等地方看起來也是一致的, 例如 GitHub。

現代的編輯器可以配置 tab 鍵的行為,使用空格來代替 tab,這樣 可以幫你處理空格的一致性問題。

要 在每個語句或者聲明后面添加一個空行。

main() {
  first(statement);
  second(statement);
}

anotherDeclaration() { ... }

不要 在聲明函數名字、操作符(operator)和 setter 以及參數名字之間添加空格。

bool convertToBool(arg) { ... }
bool operator ==(other) { ... }
set contents(value) { ... }

在關鍵字 operator 后面添加一個空格。

bool operator ==(other) => ...;

在二元和三元操作符之間添加空格。

注意, < 和 > 在表達式中使用的時候被當做二元操作符, 當作為泛型使用的時候不是。is 和 is! 都被當做一個二元操作符。 然而,. 用來訪問成員變量的時候不是二元操作符并且不 應該添加空格。

average = (a + b) / 2;
largest = a > b ? a : b;
if (obj is! SomeType) print('not SomeType');
optional([parameter = defaultValue]) { ... }

在 , 和 : 后面添加空格,當用作 map 和命名參數的情況下。

function(a, b, named: c);
[some, list, literal];
{map: literal}

不要 在一元操作符前后添加空格。

!condition
index++

在 in 關鍵字前后添加空格,在循環中的每個 ; 后面也要添加空格。

for (var i = 0; i < 100; i++) ...

for (final item in collection) ...

在流程控制關鍵字后面使用一個空格。

這條和函數以及方法調用不同,函數和方法調用 無需在名字和括號之間添加空格。

while (foo) ...

try {
  // ...
} catch (e) {
  // ...
}

不要在 (, [, 和 { 之后使用空格,也不要在 ), ], 和 } 之前使用空格。

當 < 和 > 用于泛型的時候也不要使用空格。

var numbers = <int>[1, 2, (3 + 4)];

在函數和方法體的 { 之前添加一個空格。

當 { 用于方法或者函數參數后,在和參數結尾的 ) 之間 應該添加一個空格。

getEmptyFn(a) {
  return () {};
}

把開始的大括號 ({) 放到同一行上。

class Foo {
  method() {
    if (true) {
      // ...
    } else {
      // ...
    }
  }
}

把二元符合放到多行表達式的前面一行的結尾。

雖然使用哪種風格都可以,但是為了統一性,我們通常使用 這種方式。

var bobLikesIt = isDeepFried ||
    (hasPieCrust && !vegan) ||
    containsBacon;

bobLikes() =>
    isDeepFried || (hasPieCrust && !vegan) || containsBacon;

把三元操作符放到多個表達式的下一行開始位置。

如果你在其中一個操作符之前分行,請在另外一個操作符之前也分行。

return someCondition
    ? whenTrue
    : whenFalse;

把 . 放到下一行開頭當表達式換行的時候。

someVeryLongVariable.withAVeryLongProperty
    .aMethodOnThatObject();

把構造函數初始化列表中的每個參數和值都放到同一行。

MyClass()
    : firstField = 'some value',
      secondField = 'another',
      thirdField = 'last' {
  // ...
}

注意 : 應該放到下一行并且縮進四個空格。 所有的參數應該對齊(所以每個參數縮進 六個空格)。

推薦 當無法在一行寫完集合的時候,把每個元素都用集合定義的方式來表達。(PREFER splitting every element in a collection literal if it does not fit on one line.)

在意味著在開始的括號之后和關閉括號之前以及每個 元素之后的 ,。

mapInsideList([
  {
    'a': 'b',
    'c': 'd'
  },
  {
    'a': 'b',
    'c': 'd'
  },
]);

用兩個空格來縮進代碼塊和集合體。

if (condition) {
  print('hi');

  [
    long,
    list,
    literal
  ];
}

縮進 switch case 兩個空格, case 體四個空格。

switch (fruit) {
  case 'apple':
    print('delish');
    break;

  case 'durian':
    print('stinky');
    break;
}

只少使用兩個空格來縮進多行函數級聯調用。

buffer
  ..write('Hello, ')
  ..write(name)
  ..write('!');

推薦 使用四個空格來縮進同一行的換行。

someLongObject.aReallyLongMethodName(longArg, anotherLongArg,
    wrappedToNextLine);

這也包含 => :

bobLikes() =>
    isDeepFried || (hasPieCrust && !vegan) || containsBacon;

這也有些例外情況,當表達式包含多行函數或者 集合聲明定義的時候除外。

new Future.delayed(const Duration(seconds: 1), () {
  print('I am a callback');
});

args.addAll([
  '--mode',
  'release',
  '--checked'
]);

你的目標是平衡使用縮進來顯示表達式的結構但是 又不想大量的沒必要的縮進代碼。(Your goal is to balance using indentation to show expression structure while not wanting to indent large swathes of code unecessarily.)


所屬標簽

無標簽

官方入門指南

Flutter官方發布的入門指導,包括了如何在不同的平臺(Windows, Mac, Linux)上搭建開發環境,以及一些入門級的指導,以便您從零開始進入Flutter的世界,同時,一些Flutter的框架API,也是您開發時必不可少的工具書。

從這里進入


25选5玩法中奖