改善Java文件的理由、建議和技巧

ImportNew發表於2015-08-12

我非常確定,作為開發人員我們都喜愛技術文件。我們喜歡閱讀文件、寫文件,更不用說維護文件了,我簡直愛死它了!

我也知道,每次你建立一個類或者一個方法,你都會想到要為此寫文件。我也很確定你很享受於寫文件,就像你喜歡偶爾美味的漢堡一樣。但是有時候,只是有時候,你會想要鬆懈一下,也許這次就跳過文件部分。不幸的是,這種行為會很快地失控。

所以在這篇文章中,我想聊聊這個開發者的生活中關鍵但是通常被忽視並遺忘的部分。希望你會從此愛上文件,明白你的程式碼為什麼能工作,能幫助你、你的團隊和使用你的軟體的數不盡的使用者。

為什麼文件很重要

通常,開發者都不會忘記他們兩個星期前寫的程式碼。兩個月以後甚至更長時間以後他們都會記得。即使我們保證我們從來不忘記我們寫過的任何程式碼,寫文件卻有另一個理由並且更加重要。

在寫程式碼前理清思路

我會舉一個自己的例子:我有一個開發SlideshowFX裡一個全新特性的想法,這時我就想直接開始寫程式碼並實現它。但我知道我不是做這項工程的唯一一個有激情的開發者。所以我的典型行為是這樣的:

1. 寫出以下類主體
public class BurgersManager {
}
2. 思考:“那麼,我應該在BurgersManager類中有些CRUD操作”
3. 寫下:
public…
4. 思考:“我應該返回什麼值?目前來說void就可以”
5. public void addBurger(Burger burger) {
// TODO implement that later
}
public …
6. 思考:“我應該返回被吃掉的漢堡的例項嗎?還是void就可以?就像第4步那樣。。。”
7. public void eat(Burger burger, boolean fast) {
// TODO …
8. 告訴自己:“糟糕,咖啡時間了,我的咖啡呢。。。”
9. 搜尋,喝咖啡,和同事交談
10. 然後告訴自己:“回去工作吧,我剛才在做什麼來著?”

我知道,你在這個例子中看到了自己,對吧?在創造性工作剛開始的時候,我們的思路有些混亂,所以當你直接開始寫程式碼,那麼程式碼也會很混亂。在寫程式碼之前就考慮文件能夠幫你理清思路並清除列出你要用程式碼實現的事。所以第一步應該是寫出以下程式碼:

/**
* 此類通過提供CRUD操作來管理漢堡
* 採用單件模式。可以使用{<a href='http://www.jobbole.com/members/57845349'>@link</a> #getInstance()}來獲得這個管理器的例項。
* 之後可以用以下方法來呼叫CRUD操作:
*/

{<a href='http://www.jobbole.com/members/57845349'>@link</a> #addBurger(Burger)} 用來增加漢堡,並受管理於
* 單件例項 ;
* @作者 Thierry Wasylczenko
* @版本 0.1
* <a href='http://www.jobbole.com/members/chchxinxinjun'>@since</a> BurgerQueen 1.0
*/
public class BurgersManager {

}

這就是一個簡短的例子,這個例子能夠:

  • 強迫你思考你建立的類的目的是什麼
  • 幫你確定你的需要
  • 即使是在你休息之後也能幫你想起來你在做什麼
  • 幫助你預估還有什麼是需要做的

夥計,你是在團隊中開發

你也許不是在單獨工 作,你可能有尊敬的同事,你想和那些同事一起喝咖啡聊天。重點是,因為你喜歡他們,所以你想要幫助他們參與到你那令人興奮的漢堡王的實現中去。為此,最好的做法就是確定他們在讀你的程式碼時,有完美的文件參考。即使他們在你寫程式碼之後的兩個星期問你問題,你也能毫無猶豫地回答他們。

這就是另一個為什麼文件很重要的理由:它能避免人們多次跑來問你你這複雜的演算法是怎樣運作的,或者為什麼管理器中增加的漢堡沒有同樣被加到職工管理器的統計中去。在一個團隊中,文件可以避免以下問題:

  • 在工作的時候被打斷,之後難以返回繼續工作;
  • 尋找可以回答問題的人,因為讓其他成員知道了解自己是否能夠回答問題;
  • 等待某個隊員有時間回答他們的問題。
  • 所以寫文件可以幫助團隊提高生產力並專注於開發。

讓成功更進一步

這一點更加主觀些。寫Javadoc讓我非常有成就感,因為當我再次使用我的API的時候,我寫程式碼有文件參考,這幫我確保我沒有忘記任何小細節。儘管我通常不會忘記,知道有文件在支撐我的記憶力也是件很棒的事。

看到IntelliJ IDEA展示我的文件讓我有“嘿,看,我就像是專業的,我做的東西太棒了,我甚至有文件噢”的感覺。在某些程度上的確是這樣,不是嗎?因為當你在使用一個 lib,其中的 log(String s, int i) 沒有任何命名良好的引數描述,你一定像我一樣在想“這個究竟是什麼玩意兒?”。

不知道你怎樣想的,我反正是覺得新的Javadoc設計特別贊。我認為讓自己的文件整潔是非常棒的事。但是正如我說的,這只是我個人的感受。

寫Javadoc的小技巧

在Javadoc中你有一下很好的標籤可以使用:

  • @author
  • @version
  • @param
  • @return
  • @exception/@throws
  • @see
  • @since
  • @serial/@serialField/@serialData
  • @deprecated

但是這篇文章的目的並不是詳細解釋所有標籤,而是作為文件作者和開發人員,我想分享我在寫我的Javadoc時使用的技巧。

使用@link和@linkplain來指向某些程式碼

在我的Javadoc中,如果有依賴關係或者對文件有用,我會提及其它類和方法。為了使方法和類的瀏覽更簡便,你可以使用@link。它是這樣工作的:

  • {@link BurgersManager} 指向一個類
  • {@link BurgersManager burgers manager} 指向帶有標籤的類
  • {@link #eat(Burger, boolean)} 指向此類中的某個方法
  • {@link #eat(Burger, boolean) eat} 指向此類中帶有標籤的某個方法
  • {@link BurgersManagers#eat(Burger, boolean)} 指向其他類中的某個方法
  • {@link BurgersManagers#eat(Burger, boolean) burgers manager eat} 指向其他帶有標籤的類的某個方法

@link 和 @linkplain 的區別是後者不會生成等寬字型的程式碼。

使用@code來表明程式碼段

通常你會在Javadoc中發現一段程式碼,用來說明怎樣使用方法和類,或者提供其它例子。為了正確顯示程式碼,並防止一些像這樣的標記被打斷,你可以使用@code。

{<a href='http://www.jobbole.com/members/java12'>@code</a> 
List&lt;Burger&gt; burgers = new ArrayList&lt;&gt;();
  for(int index = 0; index &lt; 10; index++) {
    burgers.add(new Burger(“Burger #” + index)); 
  }
}

@code會為你生成標記。

使用@value來在文件中插入欄位值

當你有一個常量,我可能想要它的值在文件中顯示出來。有兩個選擇:

  • 自己插入這個值。但是如果這個值改變了,你必須更新你的文件,如果你絕對不會忘記這點,那你可以放心選擇這個做法;
  • 使用@value來為你插入值,這樣你就不用手動更新你的文件。

對我來說第二個選擇是利用Javadoc工具的最佳方法,我會討論這個方法。實際上,使用單一屬性特別有用:

/**
* The default value for this field is {@value}.
* 這個域的預設值是{@value}.
*/
public static final String BURGER_SHOP_NAME = "Thierry's shop";

但你也可以指向其它常量,比如:

/**
* The default value for this field is {@value} when the value
* of {<a href='http://www.jobbole.com/members/57845349'>@link</a> #OWNER} is {@value #OWNER}.

* 這個域的預設值是{@value} 當
* {<a href='http://www.jobbole.com/members/57845349'>@link</a> #OWNER}的值為{@value #OWNER}.
*/
public static final String BURGER_SHOP_NAME = &quot;Thierry&#039;s shop&quot;;

/**
* The default owner of this awesome burger shop.

* 這家很棒的漢堡店的預設店主.
*/
public static final String OWNER = &quot; Thierry&quot;;

用@since來表明此特性的生效時間

通常,在你的程式碼中表明類或者方法何時開始生效非常有用。為此使用@since標籤並在其後註明該特性執行的版本/年份:

/**
* This awesome class is for doing awesome things
* 這個棒呆了的類是用來做些棒呆了的事
* <a href='http://www.jobbole.com/members/chchxinxinjun'>@since</a> burger-core-0.1
* @version 0.2
*/
public class BurgersManager {

/**
* Allows to eat burgers
* 可以吃漢堡
* <a href='http://www.jobbole.com/members/chchxinxinjun'>@since</a> burger-core-0.2
*/
public void eat(Burger burger, boolean fast) {
// TODO
}
}

你可以看到,我把它用在了方法和類上,並且不止包含了版本號。事實上,現在我們的應用有很多不同的模組,這些模組可以有不同生命週期,即版本。說某個方法或者類從0.2版本開始生效並沒有特別的意思。那麼究竟是什麼的0.2版本?這就是為什麼我總是用一個相關的@since 來幫助我的同事第一眼就明白這些是什麼時候開始生效的。

不止如此,這個標籤的一個好處就是它可以幫你建立釋出說明。等會兒,啥?不,並不是使用你最喜歡的IDE,比如IntelliJ IDEA,然後查詢包含“@since burger-core-0.2″的檔案。然後瞧,你可以找到自那個版本之後新增的所有方法和類。當然,這無法告訴你被更新的方法和類,而只會告訴你新新增的東西。但是你應該看到,這麼簡單的竅門多有用。

不要匿名,使用 @author

我非常討厭的一件事:開發人員不承認自己的程式碼,並且不表明是他們為了一個糟糕的原因寫了這糟糕的程式碼。如果你寫了一段程式碼,要麼承認它,要麼去當經理。你可以用 @author 來表明你是這個類或者方法的作者。我認為把這標籤既放在類上也放在方法上比較好,因為一個類的方法可能不是都是類的作者寫的。

另一個好習慣就是,把一個方法或類的所有作者都加上。 試想一下,你和你的同事寫了一個很棒的方法,而標籤表明你是這個方法的唯一作者。有一天你去度假了,有人在讀你的方法,但不是很明白並且想要一些細節。而是因為你被標為唯一的作者,他們不知道這個資訊可以從和你一起寫程式碼的同事那裡很容易就獲得。你知道我要說什麼了,對吧?要記得給程式碼加@author來表 明作者。

對非void方法要使用@return

我要說這一點對我來說非常有意義。有時候我看到類似以下例子中的程式碼就要跪了。

/** Get the address.
 * @return
 */
public String getAddress() { /* … */ }

為什麼!?說真的,為什麼你不填好@return?“因為只是一行而已,就是獲得地址”。

不不不,請不要這樣。如果你那樣回答,是因為你的文件。怎麼說呢,因為你的文件欠佳。是的,因為你可以很簡單地寫出一個更好的版本,而不是像以上你見到的糟糕的文件, 看:

/**
* Get the address of this burger shop. The address is of the following format:
* {<a href='http://www.jobbole.com/members/java12'>@code</a> address line 1
* address line 2
* zipcode city}
* @return the address of this burger shop or {<a href='http://www.jobbole.com/members/java12'>@code</a> null} if not filled.
*/

/**
*獲取漢堡店的地址。地址格式:
* {<a href='http://www.jobbole.com/members/java12'>@code</a> 地址行1
* 地址行2
* 郵編 城市}
* @return 漢堡店的地址,如果沒有填地址返回 {<a href='http://www.jobbole.com/members/java12'>@code</a> null}.
*/

好太多了,對吧?這樣你的文件就有用了。我一直試著尋找給程式碼寫文件的合適方法,因為有時候讀者只讀 @return 的內容,有時候也會讀 @return 上面的內容,你新增一些說明就可以簡單地避免疑惑。

用@param說明引數的含義

有什麼比看到方法使用一個像 i 這樣的意義不明的引數而不加任何文件更加沮喪呢?有時候你可以通過方法的名字來猜到這個引數的目的,可是有時候就不行。所以在你的文件裡,你應該使用@param來表明這個引數的含義,並說明可能的有效值。在我們的例子中,i可以是日誌的級別:INFO, DEBUG或者TRACE。這個標籤另一個很有用的例子就是當這個值對應的是一個索引。有些情況下索引從0開始,有些情況下從1開始。@param就是用來描述這一區別的標籤。

生成文件

在程式碼中有文件是非常好的,但是現在你必須生成文件。所以你可以使用JDK提供的Java文件工具來生成它。

通過執行類似這樣的命令:

javadoc {packages|source-files} [options]

你可以指定想要生成文件的包名或檔名,多個名字用空格分隔。

以下簡要描述了一些jJavadoc工具能夠接受的選項:

  • -author: 在生成的文件中生成@author用
  • -d: 要在當前目錄之外生成文件的目錄
  • -nodeprecated: 不為被標為@deprecated的程式碼生成文件
  • -protected: 包含protected和public類和類成員
  • -private: 包含private類和類成員
  • -public: 只包含public類和類成員

像IDE之類的工具也可以生成你的文件,但是如果它很好地格式化並且可以提供預覽。

一些像Maven和Gradle這樣的依賴管理工具也帶有生成文件的階段或任務。這很棒,因為你的文件可以一直緊隨程式碼的釋出來生成,這樣它就一直是最新的。

總結

文件對於你的整個團隊非常重要。它能幫你理清你在寫什麼程式碼,更重要的是,你為什麼這樣實現它。

希望這篇文章能讓你想要寫出更好的文件。如果是這樣的話請告訴我你是否寫了文件,你是怎樣寫的。

我的推特@twasyl,或者在下面留言都可以!

相關文章