iT邦幫忙

2

常見的 Java Interface 錯誤用法

  • 分享至 

  • xImage
  •  

在 Java 專案中,應該不少人看過或寫過只有一個實作(implementation)的介面 (interface),並且以 interface-impl 的風格成對出現,如下圖的 FooImpl, BarImpl, ServiceImpl:

kaisheng714.github.io-常見的 Java Interface 錯誤用法

雖然此寫法可以在任何時候用另一個實作來替換原本的實作,提高程式碼的彈性。但我認為如果當前沒有多個實作,這反而會增加額外的工作量,是一種過度設計,可能導致下列幾個問題:

問題

很多人會寫 interface-impl 的原因是根據經典的設計原則或設計模式,認為程式應依賴抽象介面,於是他們事先寫出許多 interface、提早抽象化,以便於未來替換不同實作方式,增加程式彈性。然而,現實世界中充滿了變數,專案隨著時間推移,許多過早預想的設計最終很可能無法實現。(YAGNI 原則)

此外,如果在專案中,當前的 interface 只存在唯一的 implementation,換句話說,此時的 interface 其實並不是一個抽象概念,反而具體到某種程度,這往往造成過度設計的問題,並可能導致後續接手維護程式的人感到困難。例如每當 interface-impl 之一發生改變時,無論是新增功能、重構、改變名稱或其他修改,都需要額外的工作量來同步另一方。在開發中,我們應該減少重複的工作,特別是重複維護程式碼。(DRY 原則)

在較大的專案中,如果存在許多 interface-impl,IDE 可能會妨礙進行 trace code,因為 IDE 會不斷詢問要導向至何者,這也會降低開發者的工作效率與心理感受,而成對出現的 interface-impl 表示檔案數量是比原本多一倍的,這不僅讓專案虛胖,也增加了複雜度,變得不那麼直觀。

如何改善?

我認為這種情況下,直接使用具體類別是合理的,因為保持程式碼的簡單直觀非常重要。有些人可能會覺得這樣寫也無傷大雅,但大問題通常源於小問題,最終很可能成為令眾人束手無策的歷史共業

如果需要為單元測試而使用 interface,我建議可以使用模擬(mocking)函式庫,或者利用繼承、@Override或模擬(faking)技術在測試中替換具體實作,這樣就不需要特別寫 interface,同時也能保持專案的簡潔性。

因此,我建議開發者在不確定是否需要 interface 時,可以先暫時不要。在現代強大的 IDE 的幫助下,可以在確定需要時隨時進行「extract interface」,幾乎沒有額外的成本。因此,改善這個問題的方法很簡單:延遲決定,並通過持續反饋和迭代,及時調整和改進設計

interface 的建議用法

一般而言,使用 interface 的目的是實現多型,以提高程式碼的靈活和可維護性。其中一種常見的用法是透過 property,可以在 runtime 時根據不同環境動態選擇使用哪種 implementation。

對於開發函式庫、SDK 等需要提供給外部專案使用的情況,也很適合運用 interface 定義系統邊界,這個方式可以讓外部 client 透過 interface 來整合與使用。開發者只要在設計時專注於提供規格,且可以不必在乎 client 如何實作,只需要求他們符合 interface 的規範即可。

另一方面,在實務上,若專案程式碼不對外開放(例如企業應用程式),或者不使用必須寫 interface 的框架(如微服務、ORM等),則需要使用 interface 的情況可能較少。

結語

雖然 interface 可以提高程式碼的靈活性和可維護性,如果程式在設計時就有很具體的行為,而且目前也不會有不同實作,那其實可不必寫 interface。

雖然經典的設計原則鼓勵程式之間應依賴抽象介面,但依賴具體類別也並不是錯,因此我建議開發者應根據專案的情況,權衡是否需要 interface,並且遵循最佳實踐。

Reference

本文轉錄自我的部落格 https://kaisheng714.github.io/articles/anti-pattern-of-java-interface-impl-style

更多你可能會感興趣的文章


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言