Dashboard > JAVA SE > Home > Java Programming Implementation Guidelines
JAVA SE Log In   View a printable version of the current page.
Java Programming Implementation Guidelines
Added by zach14c, last edited by zach14c on Oct 05, 2006  (view change)
Labels: 
(None)

原文引自
Thinking in Java 第二版 (中文版) - 侯捷
Thinking in Java, 3rd ed. Revision 4.0 - Bruce Eckel

Java Programming Guidelines - Implementation

  1. 一般來說,請遵守Sun的程式編寫習慣。
    你可以在以下網址找到相關文件:java.sun.com/docs/codeconv/index.html。本書儘可能遵守這些習慣。眾多Java程式員看到的程式,都是由這些習慣構成的。假如你固執地停留在過去的編寫風格中,看你的程式碼的人會很難閱讀。不論你決定採用什麼編寫習慣,請在整個程式中保持一致。你可以使用Jalopy(http://jalopy.sourceforge.net)來重整你的Java code。在這裡找到檢查你的code的工具:http://jcsc.sourceforge.net
  2. 無論使用何種編寫風格,如果你的團隊能夠加以標準化,那麼的的確會帶來顯著效果。
    標準化的價值在於,分析程式碼時所花的腦力較小,因而可以專心於程式碼的實質意義。
  3. 遵守標準的大小寫規範。
    class名稱的第一個字母應為大寫。
    資料成員、函式、物件(references)的第一個字母應為小寫。所有識別名稱的每個字都應該連在一塊兒,所有非首字的第一個字母都應該大寫。例如:ThisIsAClassName, thisIsAMethodOrFieldName。如果你在static final基本型別的定義處指定了常數初始式(constant initializers),那麼該識別名稱應該全為大寫,並且在每個字間用底線('_', underscore)連接起來,代表一個編譯期常數。Packages是個特例,其名稱皆為小寫,即使非首字的字母亦是如此。域名(com, org, net, edu等等)皆應為小寫。
  4. 不要自己發明「裝飾用的」private資料成員名稱。
    通常這種名稱的形式是在最前端加上底線和其他字元。匈牙利命名法(Hungariannotation)是其中最差的示範。在這種命名法中,你得加入額外字元來表示資料的型別、用途、位置等等。彷佛你用的是組合語言(assembly langauge)而編譯器沒有提供任何協助似的。這樣的命名方式容易讓人混淆又難以閱讀,也不易推行和維護。就讓classes和packages來進行「名稱上的範圍制定(name scoping)」吧。
  5. 當你撰寫通用性(general-purpose use)的class時,請遵守正規形式(canonical form)。
    包括equals()、hashCode()、toString()、clone()(實作出Cloneable),並實作出Comparable和Serialiable等等。
  6. 對於那些「取得或改變private欄位值」的函式,請使用JavaBeans的"get"、"set"、"is" 等命名習慣,即使你當時不認為自己正在撰寫JavaBean。這麼做不僅可以輕易以Bean的運用方式來運用你的class,也是對此類函式的一種標準命名方式,使閱讀code的人更易於理解。
  7. 為你所撰寫的每一個class,加上JUnit test.
    你不需要移除該測試碼就可將程式納入專案。而且如果有所變動,你可以輕易重新執行測試。這段程式碼也可以做為class的使用範例。
  8. 有時候你需要透過繼承,才得以存取base class的protected 成員。
    這可能會引發對多重基礎類別(multiple base types)的認知需求。如果你不需要向上轉型,你可以先衍生新的class以便執行protected存取動作,然後在「需要用到上述protected成員」的所有classes中,將新class宣告為成員物件,而非直接繼承。
  9. 避免純粹為了效率考量而使用final 函式。
    只有在程式能動但執行不夠快時,而且效能量測工具(profiler)顯示某個函式的叫用動作成為瓶頸時,才使用final 函式。
  10. 如果兩個classes因某種功能性原因而產生了關聯(例如容器containers和迭代器iterators),那麼請試著讓其中某個class成為另一個class的內隱類別(inner class)。
    這不僅強調二者間的關聯,也是透過「將class名稱巢狀置於另一個class內」而使同一個class名稱在單一package中可被重複使用。Java容器程式庫(containers library)在每個容器類別中都定義了一個內隱的(inner)Iterator class,因而能夠提供容器一份共通介面。運用內隱類別的另一個原因是讓它成為private實作物的一部份。在這裡,內隱類別會為資訊隱藏帶來好處,而不是對上述的class關聯性提供助益,也不是為了防止命名空間污染問題(namespace pollution)。
  11. 任何時候你都要注意那些高度耦合(coupling)的classes。請考慮內隱類別(inner classes)為程式撰寫和維護帶來的好處。
    內隱類別的使用並不是要去除classes間的耦合,而是要讓耦合關係更明顯也更便利。
  12. 不要成為「過早最佳化」的犧牲品。
    那會讓人神經錯亂。尤其在系統建構初期,先別煩惱究竟要不要撰寫(或避免)原生函式(nativemethods)、要不要將某些函式宣告為final、要不要調校程式效效率等等。你的主要問題應該是先證明設計的正確性,除非設計本身需要某種程度的效率。
  13. 讓範圍(作用域,scope)儘可能愈小愈好,這麼一來物件的可視範圍和壽命都將儘可能地小。
    這種作法可降低「物件被用於錯誤場所,因而隱藏難以察覺的臭蟲」的機會。例如,假設你有個容器,以及一段走訪該容器的程式片段。如果你複製該段程式碼,將它用於新的容器身上,你可能會不小心以舊容器的大小做為新容器的走訪上限值。如果舊容器已不在存取範圍內,那麼編譯期便可找出這樣的錯誤。
  14. 使用Java標準程式庫提供的容器。
    請熟悉它們的用法,你將因此大幅提昇你的生產力。請優先選擇ArrayList來處理序列(sequences),選擇HashSet來處理集合(sets)、選擇HashMap來處理關聯式陣列(associative arrays),選擇LinkedList(而不是Stack)來處理stacks和queues。
  15. 對一個強固的(robust)程式而言,每一個組成都必須強固。
    請在你所撰寫的每個class中運用Java提供的所有強固提昇工具:存取權限、異常、型別檢驗...等等。透過這種方式,你可以在建構系統時安全地移往抽象化的下一個層次。
  16. 寧可在編譯期發生錯誤,也不要在執行期發生錯誤。
    試著在最靠近問題發生點的地方處理問題。請優先在「擲出異常之處」處理問題,並在擁有足夠資訊以處理異常的最接近處理常式(handler)中捕捉異常。請進行現階段你能夠對該異常所做的處理;如果你無法解決問題,應該再次擲出異常。
  17. 當心冗長的函式定義。
    函式應該是一種簡短的、「描述並實作class介面中某個可分離部份」的功能單元。過長且複雜的函式不僅難以維護,維護代價也高。或許它嘗試做太多事情了。如果你發現這一類函式,代表它應該被切割成多個函式。這種函式也提醒你或許得撰寫新的class。小型函式同樣能夠在你的class中被重複運用。(有時候函式必須很大才行,但它們應該只做一件事情)
  18. 儘可能保持"private"。
    一旦你對外公開了程式庫的概況(method、class、或field),你便再也無法移除它們。因為如果移除它們,便會破壞某個現有的程式碼,使得它們必須重新被編寫或重新設計。如果你只公開必要部份,那麼你便可以改變其他東西而不造成傷害。設計總是會演化,所以這是個十分重要的自由度。透過這種方式,實作碼的更動對derived class造成的衝擊會降到最低。在多執行緒環境下,私密性格外重要 — 只有private 欄位可受保護而不被un-synchronized(未受同步控制)的運用所破壞。
  19. 大量運用註解,並使用javadoc的「註解文件語法」來產生程式的說明文件。
    不過,註解應該賦予程式碼真正的意義;如果只是重申程式碼已經明確表示的內容,那是很煩人的。請注意,通常Javaclass和其函式的名稱都很長,為的便是降低註解量。
  20. 避免使用「魔術數字」
    也就是那種寫死在程式碼裡頭的數字 — 如果你想改變它們,它們就會成為你的惡夢,因為你永遠都沒有辦法知道"100" 究竟是代表「陣列大小」或其他東西。你應該產生具描述性的常數名稱,並在程式中使用該常數名稱。這使程式更易於理解也更易於維護。
  21. 撰寫建構式時,請考慮異常狀態。
    最好情境下,建構式不執行任何會擲出異常的動作。次佳情境下,class只繼承自(或合成自)強固的(robust)classes,所以如有任何異常被擲出,並不需要清理。其他情況下,你就得在finally子句中清理合成後的classes。如果某個建構式一定會失敗,適當的動作就是擲出異常,使呼叫者不至於盲目認為物件已被正確產生而繼續執行。
  22. 在建構式中只做唯一必要動作:將物件設定至適當狀態。
    避免呼叫其他函式(除了final 函式),因為這些函式可能會被其他人覆寫因而使你在建構過程中得到不可預期的結果。小型而簡單的建構式比較不可能擲出異常或引發問題。
  23. 如果你的class需要在「用戶程式員用完物件」後進行清理動作,請將清理動作放到單一而定義明確的函式中。
    最好令其名稱為dispose() 以便能夠將用途告訴他人。此外請將boolean旗標放到class中,用以代表物件是否已被清理,使finalize() 得以檢驗其死亡條件(termination condition)。
  24. finalize() 只可用於物件死亡條件的檢驗,俾有益於除錯。
    特殊情況下可能需要釋放一些不會被垃圾回收器回收的記憶體。因為垃圾回收器(garbage collector)可能不會被喚起處理你的物件,所以你無法使用finalize() 執行必要的清理動作。基於這個原因,你得撰寫自己的「清理用」函式。在class finalize()中,請檢查確認物件的確已被清理,並在物件尚未被清理時,擲出衍生自RuntimeException的異常。使用這種架構前,請先確認finalize() 在你的系統上可正常運作(這可能需要呼叫System.gc() 來確認)。
  25. 如果某個物件在某個特定範圍(scope)內必須被清理(cleanedup),而不是被垃圾回收機制收回,請使用以下方法
    將物件初始化,成功後立刻進入擁有finally子句的一個try區段內。finally子句會引發清理動作。
  26. 當你在繼承過程中覆寫了finalize(),請記得呼叫super.finalize()。
    但如果你的「直接上一層superclass」是Object,就不需要這個動作。你應該讓super.finalize() 成為被覆寫(overridden)之finalize() 的最後一個動作而不是第一個動作,用以確保base class的組件在你需要它們的時候仍然可用。
  27. 當你撰寫固定大小的物件容器,請將它們轉換為陣列
    尤其是從某個函式回傳此一容器時。透過這種方式,你可以獲得陣列的「編譯期型別檢驗」的好處,而且陣列接收者可能不需要「先將陣列中的物件加以轉型」便能加以使用。請注意,容器程式庫的base class(Java.util.Collection)具有兩個toArray(),能夠達到這個目的。
  28. 在interface(介面)和abstract class(抽象類別)之間,優先選擇前者。
    如果你知道某些東西即將被設計為一個base class,你的第一選擇應該是讓它成為interface;只有在一定得放進函式或資料成員時,才應該將它改為abstract class。interface只和「用戶端想進行什麼動作」有關,class則比較把重心放在實作細節上。
  29. 為了避免一個十分令人洩氣的經驗,請確認你的classpath中的每個名稱,都只有一個未被放到packages裡頭的class。
    否則編譯器會先找到另一個名稱相同的class,並回報錯誤訊息。如果你懷疑你的classpath出了問題,試著從classpath中的每個起點搜尋同名的.class檔案。最好還是將所有classes都放到packages裡頭。
  30. 留意一不小心犯下的重載(overloading)錯誤。
    如果你覆寫baseclass 函式時沒有正確拼寫其名稱,那麼便會增加一個新的函式,而不是覆寫原有的函式。但是這種情況完全合法,所以你不會從編譯器或執行期系統得到任何錯誤訊息 — 你的程式碼只是無法正確作用,如此而已。
  31. 當心過早最佳化。
    先讓程式動起來,再讓它快 — 但只有在你必須(也就是說只有在程式被證明在某段程式碼上遭遇效能瓶頸)時才這麼做。除非你已經使用效能量測工具(profiler)找出瓶頸所在,否則你可能只是在浪費你的時間。效能調校的「隱藏成本」便是讓你的程式碼變得更不可讀、更難維護。
  32. 記住,程式碼被閱讀的時間多於它被撰寫的時間。
    清晰的設計能夠製作出易懂的程式。註解、細節說明、範例都是無價的。這些東西能夠幫助你和你的後繼者。如果沒有其他資訊,那麼從Java線上文件找出一些有用的資訊時,你所遭遇的挫敗應該足以讓你相信這一點。

Site powered by a free Open Source Project / Non-profit License (more) of Confluence - the Enterprise wiki.
Learn more or evaluate Confluence for your organisation.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.1.5a Build:#411 Mar 16, 2006) - Bug/feature request - Contact Administrators