[TOC]
在Dart中,對象具有成員,成員可以是函數(方法)或數據(實例變量)。以下最佳實踐適用于對象的成員。
## 不要把不必要地將字段包裝在getter和setter中。
在Java和c#中,通常將所有字段隱藏在getter和setter之后(或c#中的屬性),即使實現只是轉發給字段。這樣,如果您需要在這些成員中做更多的工作,您可以不需要接觸callsites。這是因為調用getter方法與訪問Java中的字段不同,訪問屬性與訪問c#中的原始字段不兼容。
Dart沒有這個限制。字段和getter /setter完全無法區分。您可以在類中公開一個字段,然后將其包裝在getter和setter中,而不必接觸任何使用該字段的代碼。
~~~
class Box {
var contents;
}
~~~
以下是不推薦的寫法:
~~~
class Box {
var _contents;
get contents => _contents;
set contents(value) {
_contents = value;
}
}
~~~
## 優先使用final字段來創建只讀屬性。
如果您有一個字段,外部代碼應該能夠看到,但不能分配給它,一個簡單的解決方案是簡單地標記它為final。
~~~
class Box {
final contents = [];
}
~~~
以下是不推薦的寫法:
~~~
class Box {
var _contents;
get contents => _contents;
}
~~~
當然,如果您需要在構造函數外部內部分配字段,那么您可能需要執行“私有字段、公共getter”模式,但是在需要之前不要這樣做。
## 考慮對簡單成員使用=>。
除了對函數表達式使用=>外,Dart還允許使用它定義成員。這種樣式非常適合于只計算并返回值的簡單成員。
~~~
double get area => (right - left) * (bottom - top);
bool isReady(num time) => minTime == null || minTime <= time;
String capitalize(String name) =>
'${name[0].toUpperCase()}${name.substring(1)}';
~~~
寫代碼的人似乎喜歡=>,但很容易濫用它,最終導致難以閱讀的代碼。如果您的聲明超過幾行或包含深度嵌套的表達式(級聯和條件運算符是常見的攻擊者),請您和所有需要閱讀您的代碼的人幫忙,并使用塊體和一些語句。
~~~
Treasure openChest(Chest chest, Point where) {
if (_opened.containsKey(chest)) return null;
var treasure = Treasure(where);
treasure.addAll(chest.contents);
_opened[chest] = treasure;
return treasure;
}
~~~
以下是不推薦的寫法:
~~~
Treasure openChest(Chest chest, Point where) =>
_opened.containsKey(chest) ? null : _opened[chest] = Treasure(where)
..addAll(chest.contents);
~~~
對于不返回值的成員,也可以使用=>。如果setter很小,并且具有使用=>的相應getter,那么這就是慣用方法。
~~~
num get x => center.x;
set x(num value) => center = Point(value, center.y);
~~~
對于非setter void成員,使用=>不是一個好主意。=>意味著“返回一個值”,因此如果您使用void成員,讀者可能會誤解它的作用。
## 在不需要的時候不要用this.以免產生歧義。
JavaScript需要明確這一點。引用當前正在執行其方法的對象上的成員,但是像dart一樣的c++、Java和c#沒有這個限制。
你唯一需要使用這個的場景是:當具有相同名稱的局部變量隱藏您想要訪問的成員時。
以下是不推薦的寫法:
~~~
class Box {
var value;
void clear() {
this.update(null);
}
void update(value) {
this.value = value;
}
}
~~~
以下是推薦的寫法:
~~~
class Box {
var value;
void clear() {
update(null);
}
void update(value) {
this.value = value;
}
}
~~~
注意,構造函數參數從不在構造函數初始化列表中隱藏字段:
~~~
class Box extends BaseBox {
var value;
Box(value)
: value = value,
super(value);
}
~~~
這看起來令人驚訝,但工作起來像你想要的。幸運的是,由于初始化了formals,這樣的代碼相對少見。
## 在可能的情況下,對字段的聲明進行初始化。
如果字段不依賴于任何構造函數參數,則可以并且應該在聲明時初始化它。它需要更少的代碼,并且確保如果類有多個構造函數,您不會忘記初始化它。
以下是不推薦的寫法:
~~~
class Folder {
final String name;
final List<Document> contents;
Folder(this.name) : contents = [];
Folder.temp() : name = 'temporary'; // Oops! Forgot contents.
}
~~~
以下是推薦的寫法:
~~~
class Folder {
final String name;
final List<Document> contents = [];
Folder(this.name);
Folder.temp() : name = 'temporary';
}
~~~
當然,如果字段依賴于構造函數參數,或者由不同的構造函數以不同的方式初始化,那么這個指導原則就不適用。