首頁  >  文章  >  Java  >  Java API 設計實踐

Java API 設計實踐

WBOY
WBOY原創
2024-08-30 06:02:02329瀏覽

API design practices for Java

作者:BJ Hargrave

了解設計 Java API 時應應用的一些 API 設計實務。一般來說,這些實踐很有用,並確保 API 可以在模組化環境中正確使用,例如 OSGi 和 Java 平台模組系統 (JPMS)。有些做法是規定性的,有些則是禁止性的。當然,其他良好的 API 設計實務也適用。

OSGi 環境使用 Java 類別載入器概念提供模組化執行階段來強制類型 可見性 封裝。每個模組都有自己的類別載入器,它將連接到其他模組的類別載入器,以共享匯出的套件並使用導入的套件。

Java 9 中引入的 JPMS 提供了一個模組化平台,使用 Java 語言規範中的存取控制概念來強制執行類型 可訪問性 封裝。每個模組定義導出哪些包,從而可以被其他模組存取。預設情況下,JMPS 層中的模組都駐留在同一個類別載入器中。

套件可以包含 API。這些 API 套件有兩個客戶端角色API 使用者API 提供者。 API 消費者使用由 API 提供者實作的 API。

在下面的設計實踐中,我們將討論包的公共部分。包的成員和類型不是公共的或受保護的(即私有或預設可訪問的),無法在包外部訪問,因此是包的實作細節。

Java 套件必須是一個有凝聚力、穩定的單元

Java 套件的設計必須確保它是一個有凝聚力穩定的單元。在模組化Java中,套件是模組之間的共用實體。一個模組可以導出一個包,以便其他模組可以使用該包。因為包是模組之間共享的單位,所以包必須是內聚的,因為包中的所有類型都必須與包的特定用途相關。像 java.util 這樣的 Grab bag 套件是不鼓勵的,因為這樣的套件中的類型通常彼此沒有關係。這種非內聚性的包可能會導致大量的依賴關係,因為包的不相關部分引用其他不相關的包,並且對包的某一方面的更改會影響依賴於該包的所有模組,即使模組可能實際上並未使用該包的該部分。已修改的套件。

由於套件是共享的單位,因此它的內容必須是眾所周知的,並且隨著套件在未來版本中的發展,所包含的 API 只會以相容的方式進行更改。這表示套件不得支援 API 超集或子集;例如,將 javax.transaction 視為內容不穩定的套件。包的使用者必須能夠知道包中可用的類型。這也意味著包應該由單一實體交付(例如,一個 jar
文件),並且不會拆分為多個實體,因為包的用戶必須知道整個包都存在。

此外,該套件必須以與未來版本相容的方式發展。因此,套件應該進行版本控制,並且其版本號碼必須根據語義版本控制規則進行演變。還有一本關於語意版本控制的 OSGi 白皮書。

但是,針對套件的主要版本更改的語義版本控制建議是有問題的。包的演進必須是功能的增加。在語義版本控制中,這是增加次要版本。當你刪除函數時,這就是對包進行不相容的更改,而不是增加主要
版本,您必須移動到新的套件名稱,使原始套件仍然相容。要了解為什麼這是重要且必要的,請參閱這篇有關 Go 語義導入版本控制的論文以及 Rich Hickey 在 Clojure/conj 2016 上的精彩主題演講。這兩篇文章都說明了遷移到新套件名稱而不是更改主要套件名稱的理由對套件進行不相容更改時的版本。

最小化包耦合

一個套件中的型別可以引用其他套件中的型別。例如,方法的參數類型和傳回類型以及欄位的類型。這種包間耦合在包上創建了所謂的 uses 約束。這意味著 API 用戶必須使用與 API 提供者相同的引用包,以便他們都能理解引用的類型。

一般來說,我們希望最小化這種包耦合,以最小化包上的使用限制。這簡化了 OSGi 環境中的接線解決方案,並最大限度地減少依賴性扇出,從而簡化了部署。

介面優先於類別

對於 API,介面優先於類別。這是一種相當常見的 API 設計實踐,對於模組化 Java 也很重要。介面的使用允許實現自由以及多種實現。介面對於將 API 使用者與 API 提供者解耦非常重要。它允許包含 API 介面的套件可供實作介面的 API 提供者和呼叫介面上的方法的 API 用戶使用。這樣,API 使用者就不會直接依賴 API 提供者。它們都只依賴 API 套件。

抽象類別有時是一種有效的設計選擇,而不是接口,但通常接口是首選,特別是因為可以將預設方法添加到接口中。

最後,API 通常需要許多小的特定類,例如事件類型和異常類型。這很好,但類型通常應該是不可變的,並且不適合 API 用戶進行子類化。

避免靜電

API 中應避免靜態。類型不應該有靜態成員。應避免靜態工廠。實例建立應該與 API 解耦。例如,API 使用者應透過依賴項注入或物件註冊表(如 OSGi 服務註冊表或 JPMS 中的 java.util.ServiceLoader)接收 API 類型的物件實例。

避免靜態也是製作可測試 API 的好習慣,因為靜態無法輕易模擬。

單例

API 設計中有時會存在單例物件。但是,對單例物件的存取不應透過靜態 getInstance 方法或靜態欄位等靜態方法進行。當需要單例物件時,該物件應由 API 定義為單例,並透過如上所述的依賴注入或物件註冊表提供給 API 使用者。

避免類別載入器假設

API 通常具有可擴充性機制,API 使用者可以提供 API 提供者必須載入的類別的名稱。然後,API 提供者必須使用 Class.forName(可能使用執行緒上下文類別載入器)來載入該類別。這種機制假定從 API 提供者(或執行緒上下文類別載入器)到 API 使用者的類別可見性。 API 設計必須避免類別載入器假設。模組化的要點之一是類型封裝。一個模組(例如,API 提供者)不得對另一個模組(例如,API 使用者)的實作細節具有可見性/可存取性。

API 設計必須避免在 API 使用者和 API 提供者之間傳遞類別名,並且必須避免有關類別載入器層次結構和類型可見性/可存取性的假設。為了提供可擴充性模型,API 設計應該讓 API 使用者將類別物件(或更好的是,實例物件)傳遞給 API 提供者。這可以透過 API 中的方法或透過物件註冊表(例如 OSGi 服務註冊表)來完成。請參閱白板圖案。

java.util.ServiceLoader 類別不在 JPMS 模組中使用時,也會受到類別載入器假設的影響,因為它假設所有提供者對於執行緒上下文類別載入器或提供的類別載入器都是可見的。儘管 JPMS 允許模組聲明來聲明模組提供或使用
,但這種假設在模組化環境中通常不成立 ServiceLoader 託管服務。

不要假設永恆

許多 API 設計僅假設物件被實例化並將其新增至 API 的建構階段,但忽略了動態系統中可能發生的銷毀階段。 API 設計應該考慮物件可以來也可以走。例如,大多數偵聽器 API 允許新增和刪除偵聽器。但許多 API 設計只假設物件被添加,而從未被刪除。例如,許多依賴注入系統無法撤回注入的物件。

在 OSGi 環境中,可以新增和刪除模組,因此能夠適應這種動態的 API 設計非常重要。 OSGi 聲明式服務規範
為 OSGi 定義了一個依賴注入模型,它支援這些動態,包括撤回注入物件。

明確記錄 API 使用者和 API 提供者的類型角色

如同簡介中所提到的,API 套件的客戶端有兩種角色:API 消費者和 API 提供者。 API 消費者使用 API,API 提供者實作 API。對於 API 中的介面(和抽象類別)類型,重要的是 API 設計必須清楚記錄哪些類型只能由 API 提供者實現,哪些類型可以由 API 使用者實現。例如,監聽器介面一般由API消費者實作
以及傳遞給 API 提供者的實例。

API 提供者對 API 使用者和 API 提供者所實現的類型的變更都很敏感。提供者必須實作 API 提供者類型中的任何新更改,並且必須了解並可能呼叫 API 使用者類型中的任何新更改。 API 使用者通常可以忽略 API 提供者類型的(相容)更改,除非 API 使用者想要更改以呼叫新函數。但 API 用戶對 API 用戶類型的變化很敏感,並且可能需要修改才能實現新功能。例如,在 javax.servlet 套件中,ServletContext 類型由 API 提供者(例如 Servlet 容器)實作。在 ServletContext 中新增方法將要求更新所有 API 提供者以實作新方法,但 API 用戶無需更改,除非他們希望呼叫新方法。然而,Servlet 類型是由 API 消費者實現的,向 Servlet 添加新方法將需要修改所有 API 消費者以實現新方法,並且還需要修改所有 API 提供者以使用新方法。因此,ServletContext 類型具有 API 提供者角色,Servlet 類型具有 API 消費者角色。

由於 API 消費者通常較多,而 API 提供者較少,因此 API 演進在考慮 API 消費者類型的更改時必須非常謹慎,而對於更改 API 提供者類型則要更加寬鬆。這是因為,您需要更改少數 API 提供者以支援更新的 API,但您不希望在 API 更新時要求許多現有 API 用戶進行更改。 API 使用者只需要在 API 使用者想要利用新 API 時進行變更。

OSGi聯盟定義了文件註解、ProviderType和ConsumerType來標記API包中類型的角色。這些註解可在 osgi.annotation jar 中找到,以便在您的 API 中使用。

結論

下次設計 API 時,請考慮這些 API 設計實務。然後,您的 API 將可在模組化 Java 和非模組化 Java 環境中使用。

以上是Java API 設計實踐的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn