存取函數的主要目的是將訪問封裝到字段,以減少代碼的耦合。集合,如數組和矢量,要比單值復雜,實現起來自然不只是需要獲取和設置成員函數。特別是因為要對集合進行增減,所以需使用存取成員函數。在集合字段的適當處加入如下存取成員函數:
成員函數類型
命名約定
示例
集合獲取函數
getCollection()
getOrderItems()
集合設置函數
setCollection()
setOrderItems()
在集合中插入一個對象
insertObject()
insertOrderItem()
從集合中刪除一個對象
deleteObject()
deleteOrderItem()
生成並且插入一個新對象到集合中
newObject()
newOrderItem()
這種方法的優點是集合被完全封裝了,允許你以後用另外一個結構,可能是鏈表或是 B 樹來取代它。
同時訪問幾個字段
存取成員函數的一個優點是,它使你能有效地執行業務規則。考慮如下一個有關形狀 (Shape) 的類的層次結構。Shape的每一個子類通過 xPosition 和 yPosition 這兩個字段表示位置,並且可以通過調用成員函數 move(Float xMovement, Float yMovement) 在屏幕上的二維坐標下移動。為達到我們的目的,圖形在任何時刻都不可以只在一個坐標軸方向上移動,而應同時沿 x 和 y 軸移動(成員函數 move() 的兩個參數中的任何一個參數都可以是 0.0)。 這就意味著 move()成員函數應該是公有的,但是 setXPosition() 和 setYPosition() 應該是私有的,被 move() 成員函數正確調用。
另一個實現方法是,引入一個可以同時更新兩個字段的設置成員函數,如下文所示。成員函數 setXPosition() 和setYPosition() 應該仍然是私有的,這樣它們不會被外部類和子類直接調用(要加入一些如下文所示的注釋來說明它們不應被直接調用)。
/** 設定圖形位置 */
protected void setPosition(Float x, Float y)
{
setXPosition(x);
setYPosition(y);
}
/** 設置 x 坐標。重要:調用 setPosition(),不是這個成員函數。*/
private void setXPosition(Float x)
{
xPosition = x;
}
/** 設置圖形的 y 坐標
重要:調用 setPosition(),不是這個成員函數。
*/
private void setYPosition(Float y)
{
yPosition = y;
}
盡可能地讓字段成為被保護 (protected) 類型,這樣只有子類可以訪問它們。僅當一個外部類需要訪問一個字段時,才將相應的獲取函數或設置函數置為公有。注意:獲取函數是公有而設置函數是私有的情況經常發生。
有時需要將設置函數設為私有以保證某個常量不變。例如,Order 類可能含有一個字段表示 OrderItem 實例的集合,含有另一個叫 orderTotal 的字段表示整個定單 (order) 的總和。orderTotal 是一個表示訂購項目子類總和的有用字段。唯一可以更新 orderTotal 值的成員函數是那些處理訂購項目集合的函數。假設那些成員函數都在 Order 中實現,那麼即使getOrderTotal() 很可能是公有,也應設 setOrderTotal() 為私有。
靜態字段,也叫類字段,應被賦予有效值,因為不能假定類的實例將在一個靜態字段被訪問之前生成。
局部變量是指在一個塊(通常是一個成員函數)內定義的對象或者數據項。一個局部變量的作用范圍是定義它的塊。局部變量的一些重要的程序設計標准集中在:
· 命名約定
· 注釋約定
· 聲明
一般說來,命名局部變量遵循與命名字段一樣的約定,即使用完整的英文描述符,任何非開頭的單詞的第一個字母要大寫。
但是為方便起見,對於如下幾個特殊的局部變量類型,這個約定可以放寬:
· 流
· 循環計數器
· 異常
當有一個單輸入和/或單輸出流在一個成員函數中被打開、使用和關閉時,通常的約定是對這些流分別采用 in 和 out[GOS96] 來命名。對於既用於輸入又用於輸出的流,采用 inOut 來命名。
一個常用的取代這種約定的方法是分別采用 inputStream,outputStream 和 ioStream 這樣的名字,而不是 in,out 和inOut,雖然這與 Sun 公司的建議相抵觸。
因為局部變量常用作循環計數器,並且它為 C/C++ 所接受,所以在 Java 編程中,可以采用 i, j 或 k 作為循環計數器[GOS96]。 若采用這些名字作為循環計數器,要始終使用它們。
一個常用的取代方法是,使用如 loopCounter 或只是 counter 這樣的名字,但是這種方法的問題是,在一個需要多個計數器的成員函數中,常常發現象 counter1 和 counter2 這樣的名字。 概括起來說,i,j,k 作為計數器時,它們可以很快被輸入,它們被廣泛的接受。
因為在 Java 代碼中異常處理也非常普遍,所以字母 e 作為一般的異常符被廣泛地接受 [GOS96]。
在 Java 中聲明和注釋局部變量有幾種約定。這些約定是:
1. 一行代碼只聲明一個局部變量。這與一行代碼應只有一個語句相一致,並使得對每個變量采用一個行內注釋成為可能。
2. 用一個行內注釋語句說明局部變量。行內注釋是一種緊接在同一行的命令代碼後,用符號 // 標注出來的單行注釋風格(它也叫“行末注釋”)。應注釋出一個局部變量用於做什麼、在哪裡適用、為什麼要用等等,使代碼易讀。
3. 僅將局部變量用於一件事。一旦將一個局部變量用於多個原因,就明顯降低了它的一致性,使它難於理解。同時也增加了代碼因為局部變量舊值的意外負面影響而產生問題的可能性,這些舊值來源於前面的代碼。的確,局部變量的重新利用需要較少的內存,因而更高效,但是復用局部變量降低了代碼的可維護性,使代碼脆弱。這常常讓由於不必分配更多內存而帶來的小節省變得不值得。
在代碼行間,如在一個 if 語句作用域內,聲明的局部變量對於不熟悉你的代碼的人來說是難於找到的。
一種取代在第一次使用局部變量之前聲明它們的方法是在代碼的前部聲明它們。函數應該簡短,參見(第 2.4.5 節“寫出簡短單獨的命令行”),所以要去代碼頂部判斷局部變量用途的工作並不是很糟。
有關成員函數參數的重要標准集中在參數應如何命名和說明。參數指成員函數的實參。
參數命名遵循與局部變量命名完全一樣的約定。對於局部變量,名字隱藏是一個問題。
示例:
customer
inventoryItem
photonTorpedo
in
e
一個可行的取代方法,例如 Smalltalk,是采用局部變量的命名約定,但在名字之前加入“a”或“an”。加上“a”或“an”有助於讓參數與局部變量和字段區分開來,避免名字隱藏的問題。這種方法較好。
示例:
aCustomer
anInventoryItem
aPhotonTorpedo
anInputStream
anException
成員函數的參數在采用 javadoc @param 標識的頭文件中注釋。應說明:
1. 參數用來做什麼。需要注釋出參數用來做什麼,以便其他開發者了解使用參數的上下文。
2. 任何約束或前提條件。 如果一個參數的值域不能被成員函數接收,則應讓調用者知道。可能一個成員函數只接收正數,或者字符數小於五的字符串。
3. 示例。如果應傳遞什麼樣的參數不明顯,那麼應該在注釋中給出一個或多個例子。
采用參數類型接口。若合適的話,不要只說明參數類型屬於哪一類,如 Object,而應說明屬於哪個接口,例如 Runnable。這樣的好處是,這種有賴於環境的方法更具體(Runnable 比 Object 更具體),或者在支持多態性(不堅持一個參數是一個類的層次結構中某個類的實例,而說明它支持一個特定的接口,這意味著它只用多態地適應你的需要即可)上是一個更好的方法。
這一章集中講述類、接口、包和編譯單元的標准和指南。類是一個可以讓對象創建的模板。類包含字段和成員函數的聲明。接口是公共標識的定義,包括成員函數和字段。使用接口的類必須支持這些函數和字段。包是一個相關類的集合。最後,編譯單元是聲明類和接口的源碼文件。因為 Java 允許編譯單元放在數據庫中,所以一個單獨的編譯單元可能不與一個源碼文件物理上直接相關。
類的一些重要標准基於:
· 命名約定
· 注釋約定
· 聲明約定
· 公共和保護接口
標准 Java 約定是使用完全的英文描述符,所有單詞的第一個字母要大寫,並且單詞中大小寫混合。
類名應是單數形式。
示例:
Customer
Employee
Order
OrderItem
FileStream
String
以下的信息應寫在文檔注釋中緊靠類的定義的前面:
1. 類的目的。開發者需要了解一個類的一般目的,以判斷這個類是否滿足他們的需求。養成一個注釋與類有關的任何好東西的習慣,例如:它是否是一個模式的一部分,或是使用它時有什麼要引起注意的限制 [AMB98]。
2. 已知的問題。如果一個類有任何突出的問題,應說明出來,讓其他的開發者了解這個類的缺點/難點。此外,還應注明為什麼不解決問題的原因。注意:如果問題僅僅針對一個成員函數,那麼它應直接與那個成員函數相聯系。
3. 類的開發/維護歷史。通常要包含一個歷史記錄表,列出日期、類的作者和修改概要。這樣做的目的是讓進行維護的程序員了解過去曾對一個類所做的修改,是誰做了什麼樣的修改。
4. 注釋出采用的不變量。不變量是指一套有關實例或類在所有“穩定”時間片內為“真”的聲明。“穩定時間片”是指在一個成員函數被對象/類調用之前和立刻調用之後的時間 [MEY88]。通過說明一個類的不變量,你讓其他的開發者了解應該如何使用一個類。
5. 並行策略。任何采用 Runnable 接口的類應充分說明它的並行策略。對許多程序員來說,並行編程是一個新的而且復雜的題目,所以需要投入一些額外的時間來確保人們能夠讀懂你的東西。說明你的並行策略以及為什麼選取這個策略而不是其它策略這很重要。常用的並行策略 [LEA97] 包括下面一些內容:同步對象;停滯 (balking) 對象;警戒 (guarded) 對象;版本 (versioned)對象;同步策略控制器;接收器。
一種讓你的類容易被理解的方法是用一致的方式來聲明它們。Java 中常用的方法是按如下順序聲明一個類:
公共成員函數
公共字段
被保護成員函數
被保護字段
私有成員函數
私有字段
[LAF97] 指出,構造函數和 finalize() 應該首先列出,可能是因為這些將會是另一個開發者為了解如何使用一個類而首先查看的成員函數。此外,因為我們有一個要求將所有字段聲明為私有的標准,所以聲明順序實際應變為:
構造函數
finalize()
公共成員函數
被保護成員函數
私有成員函數
私有字段
在每一個成員函數分組內,常將函數按照字母表的順序列出來。許多開發者在每個組內有選擇性地列出了靜態成員函數,然後列出實例成員函數,再在這兩個子分組內按照字母表的順序列出成員函數。這兩種方法都可用,你只需選用一種並且一直用它。