改善Java文檔的理由、建議和技巧
我非常確定,作為開發(fā)人員我們都喜愛技術(shù)文檔。我們喜歡閱讀文檔、寫文檔,更不用說維護文檔了,我簡直愛死它了!
我也知道,每次你創(chuàng)建一個類或者一個方法,你都會想到要為此寫文檔。我也很確定你很享受于寫文檔,就像你喜歡偶爾美味的漢堡一樣。但是有時候,只是有時候,你會想要松懈一下,也許這次就跳過文檔部分。不幸的是,這種行為會很快地失控。
所以在這篇文章中,我想聊聊這個開發(fā)者的生活中關(guān)鍵但是通常被忽視并遺忘的部分。希望你會從此愛上文檔,明白你的代碼為什么能工作,能幫助你、你的團隊和使用你的軟件的數(shù)不盡的用戶。
為什么文檔很重要
通常,開發(fā)者都不會忘記他們兩個星期前寫的代碼。兩個月以后甚至更長時間以后他們都會記得。即使我們保證我們從來不忘記我們寫過的任何代碼,寫文檔卻有另一個理由并且更加重要。
在寫代碼前理清思路
我會舉一個自己的例子:我有一個開發(fā)SlideshowFX里一個全新特性的想法,這時我就想直接開始寫代碼并實現(xiàn)它。但我知道我不是做這項工程的唯一一個有激情的開發(fā)者。所以我的典型行為是這樣的:
1. 寫出以下類主體
public class BurgersManager {
}
2. 思考:“那么,我應(yīng)該在BurgersManager類中有些CRUD操作”
3. 寫下:
public…
4. 思考:“我應(yīng)該返回什么值?目前來說void就可以”
5. public void addBurger(Burger burger) {
// TODO implement that later
}
public …
6. 思考:“我應(yīng)該返回被吃掉的漢堡的實例嗎?還是void就可以?就像第4步那樣。。。”
7. public void eat(Burger burger, boolean fast) {
// TODO …
8. 告訴自己:“糟糕,咖啡時間了,我的咖啡呢。。。”
9. 搜索,喝咖啡,和同事交談
10. 然后告訴自己:“回去工作吧,我剛才在做什么來著?”
我知道,你在這個例子中看到了自己,對吧?在創(chuàng)造性工作剛開始的時候,我們的思路有些混亂,所以當你直接開始寫代碼,那么代碼也會很混亂。在寫代碼之前就考慮文檔能夠幫你理清思路并清除列出你要用代碼實現(xiàn)的事。所以第一步應(yīng)該是寫出以下代碼:
/**
* 此類通過提供CRUD操作來管理漢堡
* 采用單件模式??梢允褂脅<a >@link</a> #getInstance()}來獲得這個管理器的實例。
* 之后可以用以下方法來調(diào)用CRUD操作:
*/
{<a >@link</a> #addBurger(Burger)} 用來增加漢堡,并受管理于
* 單件實例 ;
* @作者 Thierry Wasylczenko
* @版本 0.1
* <a >@since</a> BurgerQueen 1.0
*/
public class BurgersManager {
}
這就是一個簡短的例子,這個例子能夠:
-
強迫你思考你創(chuàng)建的類的目的是什么
-
幫你確定你的需要
-
即使是在你休息之后也能幫你想起來你在做什么
-
幫助你預估還有什么是需要做的
伙計,你是在團隊中開發(fā)
你也許不是在單獨工 作,你可能有尊敬的同事,你想和那些同事一起喝咖啡聊天。重點是,因為你喜歡他們,所以你想要幫助他們參與到你那令人興奮的漢堡王的實現(xiàn)中去。為此,最好 的做法就是確定他們在讀你的代碼時,有完美的文檔參考。即使他們在你寫代碼之后的兩個星期問你問題,你也能毫無猶豫地回答他們。
這就是另一個為什么文檔很重要的理由:它能避免人們多次跑來問你你這復雜的算法是怎樣運作的,或者為什么管理器中增加的漢堡沒有同樣被加到職工管理器的統(tǒng)計中去。在一個團隊中,文檔可以避免以下問題:
-
在工作的時候被打斷,之后難以返回繼續(xù)工作;
-
尋找可以回答問題的人,因為讓其他成員知道了解自己是否能夠回答問題;
-
等待某個隊員有時間回答他們的問題。
-
所以寫文檔可以幫助團隊提高生產(chǎn)力并專注于開發(fā)。
讓成功更進一步
這一點更加主觀些。寫Javadoc讓我非常有成就感,因為當我再次使用我的API的時候,我寫代碼有文檔參考,這幫我確保我沒有忘記任何小細節(jié)。盡管我通常不會忘記,知道有文檔在支撐我的記憶力也是件很棒的事。
看到IntelliJ IDEA展示我的文檔讓我有“嘿,看,我就像是專業(yè)的,我做的東西太棒了,我甚至有文檔噢”的感覺。在某些程度上的確是這樣,不是嗎?因為當你在使用一個 lib,其中的 log(String s, int i) 沒有任何命名良好的參數(shù)描述,你一定像我一樣在想“這個究竟是什么玩意兒?”。
不知道你怎樣想的,我反正是覺得新的Javadoc設(shè)計特別贊。我認為讓自己的文檔整潔是非常棒的事。但是正如我說的,這只是我個人的感受。
寫Javadoc的小技巧
在Javadoc中你有一下很好的標簽可以使用:
-
@author
-
@version
-
@param
-
@return
-
@exception/@throws
-
@see
-
@since
-
@serial/@serialField/@serialData
-
@deprecated
但是這篇文章的目的并不是詳細解釋所有標簽,而是作為文檔作者和開發(fā)人員,我想分享我在寫我的Javadoc時使用的技巧。
使用@link和@linkplain來指向某些代碼
在我的Javadoc中,如果有依賴關(guān)系或者對文檔有用,我會提及其它類和方法。為了使方法和類的瀏覽更簡便,你可以使用@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 的區(qū)別是后者不會生成等寬字體的代碼。
使用@code來表明代碼段
通常你會在Javadoc中發(fā)現(xiàn)一段代碼,用來說明怎樣使用方法和類,或者提供其它例子。為了正確顯示代碼,并防止一些像這樣的標記被打斷,你可以使用@code。
{<a >@code</a>
List<Burger> burgers = new ArrayList<>();
for(int index = 0; index < 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 >@link</a> #OWNER} is {@value #OWNER}.
* 這個域的默認值是{@value} 當
* {<a >@link</a> #OWNER}的值為{@value #OWNER}.
*/
public static final String BURGER_SHOP_NAME = "Thierry's shop";
/**
* The default owner of this awesome burger shop.
* 這家很棒的漢堡店的默認店主.
*/
public static final String OWNER = " Thierry";
用@since來表明此特性的生效時間
通常,在你的代碼中表明類或者方法何時開始生效非常有用。為此使用@since標簽并在其后注明該特性執(zhí)行的版本/年份:
/**
* This awesome class is for doing awesome things
* 這個棒呆了的類是用來做些棒呆了的事
* <a >@since</a> burger-core-0.1
* @version 0.2
*/
public class BurgersManager {
/**
* Allows to eat burgers
* 可以吃漢堡
* <a >@since</a> burger-core-0.2
*/
public void eat(Burger burger, boolean fast) {
// TODO
}
}
你可以看到,我把它用在了方法和類上,并且不止包含了版本號。事實上,現(xiàn)在我們的應(yīng)用有很多不同的模塊,這些模塊可以有不同生命周期,即版本。說某 個方法或者類從0.2版本開始生效并沒有特別的意思。那么究竟是什么的0.2版本?這就是為什么我總是用一個相關(guān)的@since 來幫助我的同事第一眼就 明白這些是什么時候開始生效的。
不止如此,這個標簽的一個好處就是它可以幫你創(chuàng)建發(fā)布說明。等會兒,啥?不,并不是使用你最喜歡的IDE,比如IntelliJ IDEA,然后查找包含“@since burger-core-0.2″的文件。然后瞧,你可以找到自那個版本之后添加的所有方法和類。當然,這無法告訴你被更新的方法和類,而只會告訴你新添 加的東西。但是你應(yīng)該看到,這么簡單的竅門多有用。
不要匿名,使用 @author
我非常討厭的一件事:開發(fā)人員不承認自己的代碼,并且不表明是他們?yōu)榱艘粋€糟糕的原因?qū)懥诉@糟糕的代碼。如果你寫了一段代碼,要么承認它,要么去當 經(jīng)理。你可以用 @author 來表明你是這個類或者方法的作者。我認為把這標簽既放在類上也放在方法上比較好,因為一個類的方法可能不是都是類的作者寫的。
另一個好習慣就是,把一個方法或類的所有作者都加上。 試想一下,你和你的同事寫了一個很棒的方法,而標簽表明你是這個方法的唯一作者。有一天你去度假了,有人在讀你的方法,但不是很明白并且想要一些細節(jié)。而 是因為你被標為唯一的作者,他們不知道這個信息可以從和你一起寫代碼的同事那里很容易就獲得。你知道我要說什么了,對吧?要記得給代碼加@author來 表 明作者。
對非void方法要使用@return
我要說這一點對我來說非常有意義。有時候我看到類似以下例子中的代碼就要跪了。
/** Get the address.
* @return
*/
public String getAddress() { /* … */ }
為什么?。空f真的,為什么你不填好@return?“因為只是一行而已,就是獲得地址”。
不不不,請不要這樣。如果你那樣回答,是因為你的文檔。怎么說呢,因為你的文檔欠佳。是的,因為你可以很簡單地寫出一個更好的版本,而不是像以上你見到的糟糕的文檔, 看:
/**
* Get the address of this burger shop. The address is of the following format:
* {<a >@code</a> address line 1
* address line 2
* zipcode city}
* @return the address of this burger shop or {<a >@code</a> null} if not filled.
*/
/**
*獲取漢堡店的地址。地址格式:
* {<a >@code</a> 地址行1
* 地址行2
* 郵編 城市}
* @return 漢堡店的地址,如果沒有填地址返回 {<a >@code</a> null}.
*/
好太多了,對吧?這樣你的文檔就有用了。我一直試著尋找給代碼寫文檔的合適方法,因為有時候讀者只讀 @return 的內(nèi)容,有時候也會讀 @return 上面的內(nèi)容,你添加一些說明就可以簡單地避免疑惑。
用@param說明參數(shù)的含義
有什么比看到方法使用一個像 i 這樣的意義不明的參數(shù)而不加任何文檔更加沮喪呢?有時候你可以通過方法的名字來猜到這個參數(shù)的目的,可是有時候就不行。所以在你的文檔里,你應(yīng)該使用 @param來表明這個參數(shù)的含義,并說明可能的有效值。在我們的例子中,i可以是日志的級別:INFO, DEBUG或者TRACE。這個標簽另一個很有用的例子就是當這個值對應(yīng)的是一個索引。有些情況下索引從0開始,有些情況下從1開始。@param就是用 來描述這一區(qū)別的標簽。
生成文檔
在代碼中有文檔是非常好的,但是現(xiàn)在你必須生成文檔。所以你可以使用JDK提供的Java文檔工具來生成它。
通過執(zhí)行類似這樣的命令:
javadoc {packages|source-files} [options]
你可以指定想要生成文檔的包名或文件名,多個名字用空格分隔。
以下簡要描述了一些jJavadoc工具能夠接受的選項:
-
-author: 在生成的文檔中生成@author用
-
-d: 要在當前目錄之外生成文檔的目錄
-
-nodeprecated: 不為被標為@deprecated的代碼生成文檔
-
-protected: 包含protected和public類和類成員
-
-private: 包含private類和類成員
-
-public: 只包含public類和類成員
像IDE之類的工具也可以生成你的文檔,但是如果它很好地格式化并且可以提供預覽。
一些像Maven和Gradle這樣的依賴管理工具也帶有生成文檔的階段或任務(wù)。這很棒,因為你的文檔可以一直緊隨代碼的發(fā)布來生成,這樣它就一直是最新的。
總結(jié)
文檔對于你的整個團隊非常重要。它能幫你理清你在寫什么代碼,更重要的是,你為什么這樣實現(xiàn)它。
希望這篇文章能讓你想要寫出更好的文檔。如果是這樣的話請告訴我你是否寫了文檔,你是怎樣寫的。
我的推特@twasyl,或者在下面留言都可以!