採用 Javadoc 形式的整合文檔有利有弊

來源:互聯網
上載者:User
ado

Javadoc 工具可以協助產生好的 API 文檔,然而大多數 Java API 文檔卻很糟糕。因為它是原始碼的一部分,所以 API 的文檔編製職責最終還是落到了工程師身上。

在本文中,Brian 對 Java 文檔編製實踐的目前狀態進行了嚴厲的批評,同時提供了一些關於如何編寫更有用的 Javadoc 的準則。

對於大多數 Java 類庫來說,Javadoc 是唯一的文檔。而且,除了商業軟體組件之外,許多 Java 類不會用到 Javadoc。雖然 Javadoc 作為 API 參考工具很出色,但對於瞭解類庫是如何組織的和應該如何使用它來說,它卻是一種十分差勁的方法。並且即便用了 Javadoc,它通常只包含有關方法完成了什麼的最基本資料,而忽略了諸如錯誤處理、參數及傳回值的範圍和範圍、安全執行緒、鎖定行為、前置條件、後置條件、不變條件或副作用之類的重要特性。

向 Javadoc 學習

對於包括大多數開放源碼包和大多數內部開發的組件在內的許多 Java 工具而言,實際情況是:包括 Javadoc 在內,幾乎所有類庫或組件都不具有有效文檔。這就意味著開發人員要從 Javadoc 學習使用工具,而且我們應該考慮根據這一現實組織我們的 Javadoc。我經常開玩笑說:現在,Java 程式員需要具備的最重要的技能之一是熟練地使用 Google 和 Javadoc 來對那些文檔編製得十分糟糕的 API 進行“逆向工程”。這可能是真的,但卻並不十分好笑。

大多數 Java 包都有某種“根”對象,它是在得到該工具內的任何其它對象之前,必須建立的第一個對象。在 JNDI 中,該根對象是 Context,而在 JMS 和 JDBC 中,它是 Connection。如果有人告訴您 JDBC 中的基礎對象是 Connection,以及如何獲得這一對象,那麼接著您很可能會從 Javadoc 中通過仔細察看 Javadoc 中可用的方法列表找到如何建立並執行 Statement,以及如何迭代產生的 ResultSet。但您如何知道獲得 Connection 是您的第一步呢?Javadoc 在包內按照字母順序組織類,在類中按照字母順序組織方法。遺憾的是,Javadoc 中沒有神奇的“從這裡開始(Start Here)”記號把讀者帶到瀏覽 API 的邏輯開始位置。

包描述

最接近“從這裡開始”記號的是包描述,但它卻很少得到有效使用。如果將檔案 package.html 與原始碼一起放在一個包中,那麼標準的 doclet 會將已產生的 package-summary.html 檔案中的內容連同類列表一起放在該包內。遺憾的是,產生我們都很熟悉的 HTML 文檔的標準 doclet 卻無法使包描述易於找到。如果您單擊左上窗格中的某個包,那麼這會在左下窗格中產生方法列表,但並不會在主窗格中產生包的摘要 ? 必須單擊左下窗格中的包名稱來查看摘要。但不要緊,畢竟大多數包並沒有包描述。

包文檔是一個放置“從這裡開始”文檔的極好的地方,這一文檔用來概述包做什麼、主要摘要是什麼以及從何處開始瀏覽包的 Javadoc。

類文檔

除包文檔之外,特定於類的文檔對於協助使用者徹底瞭解新工具也能起到重要的作用。類文檔當然應該包括此特定類做什麼的描述,但還應該描述該類與包中的其它類如何關聯,特別是要標識任何與該類相關的工廠類。例如,JDBC 中的 Statement 類文檔應該說明:Statement 是通過 Connection 類的 createStatement() 方法獲得的。這樣,如果一個新使用者偶然進入 Statement 頁面,那麼他會發現首先他需要獲得 Connection。對每個類都應用這一約定的包會迅速為使用者指出根對象,使用者因而能夠得心應手。

因為 Javadoc 是圍繞對特定類進行文檔編製而設計的,因此在 Javadoc 中通常沒有明顯的位置來放置示範幾個相關類一起使用的範例程式碼。但由於一味地側重於特定類或方法的文檔編製,我們失去了討論如何組合包中內容的機會。如果對於根對象,在包文檔或類文檔中有一個示範一些基本用法的簡單程式碼範例,則對於許多使用者來說,將是非常有用的。例如,Connection 類文檔可以有一個簡單樣本,該樣本擷取串連、建立先行編譯語句、執行該語句并迭代結果集。從技術上說,這可能不屬於 Connection 頁面,因為它還描述了包中的其它類。然而,尤其是當結合了上面那種引用當前類所依賴的類的技術時,使用者才能非常迅速地找到擷取簡單的實用樣本的途徑,不管類的組織方式如何。

糟糕的文檔 == 糟糕的代碼

對於大多數 Java 類庫來說,除了那些作為打包組件出售的商業產品之外,要麼沒有 Javadoc,要麼非常糟糕。由於存在的事實是對於大多數包來說,Javadoc 是我們擁有的唯一文件,這基本上意味著使我們自己陷入了這樣的困境:除了作者之外,其他人沒法使用我們的大部分代碼 ? 如果不付出重大的“考古”一樣的努力,至少會這樣。

由於文檔現在是代碼的一部分,因此我認為是軟體工程社區形成一個共識的時候了,這就是,即使代碼很出色,如果文檔很糟糕,也應該被認為是差勁的代碼,因為不能有效地重用。單元測試不久前還聲譽不佳,只是到了最近它才受到了許多工程師的青睞,就和它一樣,為了改善我們生產的軟體的可靠性和可重用性,API 文檔也必須成為開發過程的一個整合部分。

編寫 Javadoc 就是某種形式的代碼檢查

編寫合理的 Javadoc 也會產生副作用,它迫使我們進行某種形式的代碼檢查,來研究類的體繫結構和它們之間的關係。如果單個包、類或方法很難編製文檔,那麼或許可以嘗試同時對多個包、類或方法進行文檔編製,這應該是個提示,即可能它需要重新設計。

文檔的自我檢查方面使得某些方面更加重要,即在開發過程中儘早編寫 Javadoc,然後隨著代碼的不斷開發,定期對其進行檢查,而不是僅僅等待程式碼完成再編寫文檔(如果有剩餘時間的話)。後一種策略十分常見,它將編寫文檔拖到項目最後,而那時時間安排十分緊張,開發人員的壓力也很大。結果再常見不過了,就是圖 1 所示的那種一文不值的文檔,它只提供了“文檔假象”。使用者真正需要的是瞭解該類的工作原理,而該文檔卻沒有提供任何這樣的資訊。

清單 1. 典型的一文不值的 Javadoc

/*** Redivsents a command history*/public class CommandHistory {/*** Get the command history for a given user*/public static CommandHistory getCommandHistory(String user) {. . .}}

那麼好的文檔包括哪些內容呢?

上面描述的組織技術(在類描述中引用相關類或工廠類,也包括了包概述和代碼樣本)是形成優秀文檔的好開端。它有助於新使用者使用 Javadoc 瞭解新工具。

但體繫結構的概述只完成了任務的一半。另一半則是詳細地解釋方法做什麼和不做什麼、在什麼條件下運行以及它們如何處理錯誤條件。大多數 Javadoc 都沒有完全提供所需的資訊,即便是那些充分描述了方法在期望情況下的行為的 Javadoc 也是如此,這些缺少的資訊包括:

方法如何處理錯誤條件或不合要求的輸入

如何將錯誤條件傳回給調用者

可能會拋出哪個特定異常的子類

哪些值對於輸入是有效

類不變條件、方法前置條件或方法後置條件

副作用

在方法之間是否有重要聯結

類如何處理多個線程同時訪問一個執行個體的情況。

Javadoc 約定提供了 @param 標記,它讓我們除了能夠對參數的名稱和類型編製文檔之外,還可以對其意義編製文檔。然而,並不是所有的方法都能很好地接受參數的任何值。例如,雖然可以合法地向任何擷取對象參數的方法傳遞空值(null)而不違反類型檢查規則,但並不是所有的方法都能在傳入空值時正常工作。Javadoc 應該顯式地描述有效參數範圍,如果它希望某個參數非 null,那麼它應該這樣描述,而如果它期望參數值在某個範圍內,例如某種長度的字串或大於 0 的整數,那麼它也應該那樣描述。並非所有方法都仔細檢查其參數的有效性;不進行有效性檢查也沒有編製關於可接受的輸入範圍的文檔,這二者的結合為災難埋下了隱患。

傳回碼

Javadoc 使得描述傳回值的意義變得很容易,但正如方法參數一樣,@return 標記應該包括對可能返回的值範圍的詳細描述。對於對象取值的傳回型別而言,它會返回空值嗎?對於整數取值的傳回型別而言,結果會限制在一個已知值或非負值的集合上嗎?任何傳回碼都有特殊意義嗎,例如從 java.io.InputStream.read() 返回 -1 表示檔案結束符?傳回碼會被用來表示例如如果無法找到表項則返回空值那樣的錯誤條件嗎?

異常

標準 doclet 複製方法的 throws 子句,但 Javadoc @throws 標記應該更為具體。例如,NoSuchFileException 是 IOException 的子類,但 java.io 中的大多數方法卻只被聲明為拋出 IOException。然而,方法可能獨立於其它 IOException 而拋出 NoSuchFileException,這是調用者要瞭解的很有用的事實 ? 它應該被包括在 Javadoc 中。還應該指出拋出各種異常類的實際錯誤條件,以便調用者知道在給定異常被拋出時該採取什麼矯正措施。應該用 @throws 標記對方法可能拋出的每個經檢查的或未經檢查的異常編製文檔,並對引發拋出異常的條件編製文檔。

前置條件、後置條件和不變條件

當然,您應該對方法對對象狀態的影響編製文檔。但您可能需要編製得更詳細一些,描述方法的前置條件、後置條件和類不變條件。前置條件是在調用方法前對對象狀態的約束;例如,調用 Iterator.next() 的前置條件是 hasMore() 為真。後置條件是方法調用完成後對對象狀態的約束,例如在調用 add() 之後 List 不可為空。不變條件是對對象狀態的一種約束,它保證該狀態始終為真,例如 Collection.size() == Collection.toArray().length()。

諸如 jContract 之類的按契約設計(Design-by-contract)工具允許您使用特殊注釋指定前置條件、後置條件和類不變條件,這類工具然後產生額外代碼來強制這些約束。無論您是否使用工具來強制這些期望條件,對這些約束編製文檔可以讓使用者知道要安全地使用類,他們可以做些什麼。

副作用

有時候,方法除了改變對象狀態之外還會有其它副作用,例如改變相關對象、JVM 或底層計算平台的狀態。例如,所有執行 I/O 的方法都有副作用。有些副作用是無害的,例如保留類處理的請求的記數。另外一些副作用則會對程式效能和正確性產生重大影響,例如修改傳遞給方法的對象的狀態,或儲存對該對象的引用的副本。諸如修改相關對象的狀態或儲存對作為方法參數傳遞的對象的引用之類的副作用應該編製文檔。

方法聯結

方法聯結意味著類中的兩個方法相互依賴,並且都對對方的行為做了假定。發生方法聯結的一種常見情形是:方法在內部使用同一個類的 toString 方法,並假定 toString 將以特別的方法格式化對象狀態。如果該類已經子類化並且 toString 方法被重寫了,那麼這種情形可能引起問題;另一個方法會突然不能正常工作,除非它也被重寫。如果您的方法依賴於其它方法的實現行為,那麼需要對那些依賴性編製文檔。而且,如果類已子類化,那麼可以以一致的方式重寫兩種方法以便使子類仍能正常工作。

安全執行緒

應該編製文檔的最重要的行為之一是安全執行緒,而對它幾乎從未編製文檔。這個類是安全執行緒的嗎?如果不是,那麼是否可以通過用同步封裝調用來使其安全執行緒嗎?這些同步必須同特定管程相關聯,還是任何一直使用的管程都可以使用呢?方法獲得了對於類外部是可見的對象的鎖嗎?

安全執行緒實際上不是二進位屬性;安全執行緒有幾種可標識的等級。對安全執行緒編製文檔,或者甚至確定安全執行緒的等級並非總是很容易。但未能進行這一工作將導致嚴重的問題;在並發應用程式中使用非安全執行緒類可能引起零星的故障,這些故障常常直到部署時才出現(那時暴露應用程式以便裝入)。而且將額外鎖定封裝在已經是安全執行緒的類周圍會影響效能,甚至引起死結。

Josh Bloch 在他的 Effective Java Programming Language Guide(參閱參考資料)一書中對類的安全執行緒等級編製文檔提供了有用的分類法。可以按照安全執行緒遞減順序將類歸到下列某一組:不可變、安全執行緒、有條件的安全執行緒、線程相容和線程對立。

這種分類是一個極佳的架構,用於在並發訪問情況下傳遞關於類行為的重要訊息。不管您是否使用這一分類法都沒關係,但您應該標識您的類意圖顯示的安全執行緒等級。我還建議:如果方法獲得對一個對象的鎖定,而該對象對於類自身的代碼外部是可見的,那麼您也應該就此編製文檔,即使這隻是一個“實現細節”,以便協助做出全域鎖定順序(global-lock-order)決策並防止死結。

結束語

對類的行為編製文檔遠遠不只是對每個方法做什麼給出一行描述。有效 Javadoc 應該包括對下列內容的描述:

類如何相互關聯

方法如何影響對象的狀態

方法如何將出錯條件通知它們的調用者以及它們可能通知什麼錯誤

類如何處理多線程應用程式中的使用

方法的參數範圍及其傳回值的範圍

另外,糟糕的文檔(或甚至更糟糕,沒有文檔)會導致優秀的代碼不可用或不可重用。通過在文檔上花一些額外時間,您將為您的使用者(可能是您自己)避免無數的挫折。



相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。