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





















