[TOC]
成員屬于對象,可以是方法變量也可以是實例變量。
## 優先使用final字段和頂級變量。
不可變的狀態 - 隨著時間的推移不會改變 - 程序員更容易推理。最小化它們使用的可變狀態量的類和庫往往更容易維護。
當然,擁有可變數據通常很有用。但是,如果您不需要它,您的默認值應該是盡可能地創建字段和頂級變量為final。
## 請使用getter進行概念上訪問屬性的操作。
決定一個成員什么時候應該是getter,什么時候應該是方法,這是一個具有挑戰性的、微妙的、但卻是好的API設計的重要部分,因此這是一個很長的指導原則。其他一些語言的文化則回避取值器。他們只在操作與實際操作幾乎完全相同的情況下才使用它們——它對完全依賴于對象的狀態進行了極少量的計算。任何比這更復雜或更重量級的東西都會在名稱后面跟隨(),以表示“在這里需要計算!”因為a.后面有一個名稱。意思是“字段”。
Dart不是那樣的。在Dart中,所有帶點的名稱都是可能進行計算的成員調用。字段是特殊的——它們是由語言提供實現的getter。換句話說,在Dart中,getter不是“特別慢的字段”;字段是“特別快速的getter”。
即便如此,選擇getter而不是方法會向調用者發送重要信號。粗略地說,信號是這個操作是“字段一樣的”。就調用者所知,該操作至少在原則上可以使用字段實現。這意味著:
即便如此,選擇方法上的getter會向調用者發送一個重要信號。大致的信號是操作是“像字段一樣”。至少在原理上,操作可以使用字段來實現,只要調用者者知道。這意味著:
* 該操作不接受任何參數并返回結果。
* 調用者主要關心結果。如果你想調用者擔心如何操作產生的結果比他們多一點,結果被生產,然后給操作描述工作一個動詞的名字,使之方法。
但這并不意味著該操作必須是在為了一個getter特別快。IterableBase.length是O(n),那沒關系。對于取值器進行重要計算是好的。但是如果它做了大量的工作,你可能需要通過使它成為一個描述其功能的動詞的方法來引起他們的注意。
~~~
【bad】
connection.nextIncomingMessage; // Does network I/O.
expression.normalForm; // Could be exponential to calculate.
~~~
* 該操作沒有用戶可見的副作用。訪問實際字段不會改變程序中的對象或任何其他狀態。它不會產生輸出,寫入文件等。一個getter也不應該做那些事情。
“用戶可見”部分很重要。取值器修改隱藏狀態或產生帶外副作用是很好的。Getters可以懶惰地計算并存儲他們的結果,寫入緩存,記錄東西等。只要調用者不關心副作用,它可能就好了。
~~~
【bad】
stdout.newline; // Produces output.
list.clear; // Modifies object.
~~~
* 該操作是冪等的。“冪等”是一個奇怪的詞,在這種情況下,基本上意味著每次調用操作多次會產生相同的結果,除非在這些調用之間明確修改某些狀態。(顯然,list.length如果在調用之間向列表中添加元素,則會產生不同的結果。)
這里的“相同結果”并不意味著getter必須在連續調用中逐字地生成相同的對象。要求這將迫使許多吸氣者進行脆弱的緩存,這否定了使用取值器的全部意義。每次調用它時,吸氣劑返回一個新的未來或列表是很常見的,而且非常好。重要的是,未來完成相同的值,列表包含相同的元素。
換句話說,結果值應該與調用者關心的方面相同。
~~~
【bad】
DateTime.now; // New result each time.
~~~
* 生成的對象不會公開所有原始對象的狀態。 字段僅顯示一個對象。如果您的操作返回一個公開原始對象的整個狀態的結果,那么它最好作為一個to___()或一個as___()方法。
如果以上所有描述了您的操作,它應該是一個取值器。似乎很少有成員會幸免于難,但令人驚訝的是很多成員。許多操作只是對某些狀態進行一些計算,其中大多數可以而且應該是getter。
~~~
rectangle.area;
collection.isEmpty;
button.canShow;
dataSet.minimumValue;
~~~
## 請將setter用于從概念上改變屬性的操作。
在setter與方法之間做出決定類似于在getter與方法之間做出決定。在這兩種情況下,操作應該是“類似字段”。
對于setter,“類似字段”意味著:
* 該操作采用單個參數,不會產生結果值。
* 該操作會更改對象中的某些狀態。
* 該操作是冪等的。對于相同的值,使用相同的值調用相同的setter兩次,就調用者而言,第二次不應該執行任何操作。在內部,也許你有一些緩存失效或記錄正在進行。沒關系。但從取值器的角度來看,似乎第二次取值什么也沒做。
~~~
rectangle.width = 3;
button.visible = false;
~~~
## 不要在沒有相應取值器的情況下定義設置器。
用戶將getter和setter視為對象的可見屬性。可以寫入但未被看到的“dropbox”屬性令人困惑,并且混淆了他們對屬性如何工作的直覺。例如,沒有getter的setter意味著你可以=用來修改它,但不是+=。
本指南也并不意味著你應該添加一個getter只是允許您要添加的設置器。對象通常不應該暴露出比他們需要更多的狀態。如果某個對象的某個狀態可以修改但不能以相同的方式公開,請改用方法。
> 這條規則有一個例外。一個 Angular組件類可以暴露是從模板調用以初始化該組件設置器。通常,這些setter不是要從Dart代碼調用的,也不需要相應的getter。(如果從Dart代碼中使用它們,它們應該有一個吸氣劑。)
>
## 避免返回null從成員返回類型為bool,double,int,或num。
即使Dart中所有類型都可以為空,但用戶認為這些類型幾乎從不包含null,而小寫名稱則鼓勵“Java原始”思維模式。
在API中有一個“可以為空的原語”類型可能偶爾會有用,例如,表示地圖中某些鍵沒有值,但這些應該很少見。
如果您確實有可能返回的此類成員null,請將其記錄得非常清楚,包括null將返回的條件。
## 避免只是為了啟用流暢的接口從方法中返回this值。
方法級聯是鏈接方法調用的更好解決方案。
~~~
var buffer = StringBuffer()
..write('one')
..write('two')
..write('three');
~~~
以下是反例:
~~~
var buffer = StringBuffer()
.write('one')
.write('two')
.write('three');
~~~