可怕的DefaultAbstractHelperImpl
不久前,我們發布了這個有趣的游戲,我們稱之為Spring API Bingo 。 當形成有意義的類名時,它是對Spring的巨大創造力的贊美和奉承。
- FactoryAdvisorAdapterHandlerLoader
- ContainerPreTranslatorInfoDisposable
- BeanFactoryDe??stinationResolver
- LocalPersistenceManagerFactoryBean
上面的兩個類實際上存在。 你能發現他們嗎? 如果沒有,請玩Spring API Bingo !
顯然,Spring API遭受...
命名事物
在計算機科學中只有兩個難題。 緩存失效,命名和錯誤一處
– Tim Bray引用Phil Karlton
在Java軟件中很難擺脫其中的幾個前綴或后綴。 考慮一下最近在Twitter上的討論,這不可避免地導致了(非常)有趣的討論 :
具有接口:PaymentService實現:PaymentServiceImpl該測試應稱為PaymentServiceImplTest而不是PaymentServiceTest
— Tom Bujok(@tombujok) 2014年10月8日
是的, Impl后綴是一個有趣的話題。 我們為什么要擁有它,為什么我們要一直這樣命名呢?
規格與主體
Java是一種古怪的語言。 在其發明之初,面向對象是一個熱門話題。 但是過程語言也具有有趣的功能。 當時,一種非常有趣的語言是Ada (以及主要來自Ada的PL / SQL)。 Ada(如PL / SQL)在包中合理地組織了過程和功能,有兩種形式:規范和主體。 從維基百科示例 :
-- Specification package Example isprocedure Print_and_Increment (j: in out Number); end Example;-- Body package body Example isprocedure Print_and_Increment (j: in out Number) isbegin-- [...]end Print_and_Increment;begin-- [...] end Example;您始終必須這樣做,并且兩件事的名稱完全相同: Example 。 它們存儲在兩個不同的文件中,分別稱為Example.ads (ad用于表示Ada,s用于說明)和Example.adb (用于主體)。 PL / SQL遵循該命令,并使用pk作為Package來命名包文件Example.pks和Example.pkb 。
Java采取了不同的方式,主要是因為多態性和類的工作方式:
- 類既是規范又是主體
- 接口的名稱不能與實現類的名稱相同(大多數情況下,當然有很多實現)
特別是,類可以是純規范的混合體,具有部分主體(抽象時)和完整規范和主體(具體時)。
這如何轉換為Java命名
并非所有人都對規范和機身的清晰分離感到贊賞,這當然可以爭論。 但是,當您處于那種具有Ada風格的思維方式時,那么您可能想要為每個類(至少在公開API的任何地方)都使用一個接口。 我們對jOOQ進行了相同的操作 ,在其中我們建立了以下策略來命名事物:
* Impl
與相應接口成1:1關系的所有實現(主體)都以Impl為后綴。 如果可能的話,我們嘗試將那些實現保留為私有包,并因此密封在org.jooq.impl包中。 例如:
- Cursor及其對應的CursorImpl
- DAO及其對應的DAOImpl
- Record及其對應的RecordImpl
這種嚴格的命名方案可以立即清楚地表明,哪個是接口(因此是公共API),哪個是實現。 從這個方面來說,我們希望Java更像Ada,但是我們擁有出色的多態性,并且…
抽象*
…并導致在基類中重用代碼。 眾所周知,常見的基類應該(幾乎)總是抽象的。 僅僅是因為它們最經常是其相應規范的不完整實現(實體)。 因此,我們有很多局部實現,它們也與對應的接口處于1:1關系,并為它們加上Abstract前綴。 通常,這些部分實現也是私有包的,并??密封在org.jooq.impl包中。 例如:
- Field及其對應的AbstractField
- Query及其對應的AbstractQuery
- ResultQuery及其對應的AbstractResultQuery
特別地, ResultQuery是擴展 Query的接口,因此AbstractResultQuery是擴展 AbstractQuery的部分實現,后者也是部分實現。
在我們的API中完全實現部分實現是很有意義的,因為我們的API是內部DSL(特定于域的語言) ,因此無論具體Field實際Substring如何,都有成千上萬種始終相同的方法,例如Substring
默認*
我們做與接口相關的所有API。 在流行的Java SE API中,已經證明這種方法非常有效,例如:
- 館藏
- 流
- JDBC
- DOM
我們還做與接口相關的所有SPI(服務提供商接口) 。 就API的發展而言,API和SPI之間存在一個本質區別:
- API是由用戶消費 ,難以實現
- SPI由用戶實現 ,幾乎不消耗
如果您不開發JDK(因此沒有完全瘋狂的向后兼容規則 ),則通常可以安全地向API接口添加新方法。 實際上,我們在每個次要版本中都這樣做,因為我們不希望任何人實現我們的DSL(誰想要實現Field的 286方法或DSL的 677方法。這太瘋狂了!)
但是SPI是不同的。 每當您為用戶提供SPI(例如帶有*Listener或*Provider后綴)時,您都不能簡單地向他們添加新方法-至少不是在Java 8之前,因為這會破壞實現,并且其中有很多。
好。 我們仍然這樣做,因為我們沒有那些JDK向后兼容規則。 我們有更多的放松 。 但是我們建議用戶不要直接自己實現接口,而應擴展一個Default實現,該實現為空。 例如ExecuteListener和對應的DefaultExecuteListener :
public interface ExecuteListener {void start(ExecuteContext ctx);void renderStart(ExecuteContext ctx);// [...] }public class DefaultExecuteListener implements ExecuteListener {@Overridepublic void start(ExecuteContext ctx) {}@Overridepublic void renderStart(ExecuteContext ctx) {}// [...] }因此, Default*是一個前綴,通常用于提供API使用者可以使用和實例化或SPI實現者可以擴展的單個公共實現,而不會冒向后兼容的風險。 對于Java 6/7缺少接口默認方法,這幾乎是一種解決方法,這就是為什么前綴命名更加合適的原因。
此規則的Java 8版本
實際上,這種做法表明,指定Java-8兼容SPI的“好”規則是使用接口,并使所有方法默認為空。 如果jOOQ不支持Java 6,我們可能會這樣指定ExecuteListener :
public interface ExecuteListener {default void start(ExecuteContext ctx) {}default void renderStart(ExecuteContext ctx) {}// [...] }*實用程序或*助手
好的,這里是供模擬/測試/覆蓋專家和愛好者使用的工具。
為各種靜態實用程序方法進行“轉儲”是完全可以的 。 我的意思是, 您當然可以成為面向對象的警察的成員 。 但…
請。 不要成為“那個家伙”!
因此,有多種識別實用程序類的技術。 理想情況下,您應遵循命名約定,然后再遵守。 例如* Utils 。
從我們的角度來看,理想情況下,您甚至只轉儲所有未嚴格綁定到單個類中特定領域的實用程序方法,因為坦率地說,您最后一次欣賞何時必須經歷數百萬個類才能找到該實用程序方法? 決不。 我們有org.jooq.impl.Utils 。 為什么? 因為它將允許您執行以下操作:
import static org.jooq.impl.Utils.*;這樣,幾乎感覺就像您在整個應用程序中都具有“頂級功能”之類的東西。 “全局”功能。 我們認為這是一件好事。 而且我們完全不贊成“我們不能嘲笑這一論點”, 因此甚至不要嘗試開始討論。
討論區
…或者實際上,讓我們開始討論。 您的技術是什么,為什么? 以下是湯姆·布約克(Tom Bujok)原始Tweet的一些反應,可幫助您入門:
@tombujok號PaymentServiceImplTestImpl!
— Konrad Malawski(@ktosopl) 2014年10月8日
@tombujok擺脫了界面
— Simon Martinelli(@simas_ch) 2014年10月8日
@tombujok Impl一切!
— Bartosz Majsak(@majson) 2014年10月8日
@tombujok @lukaseder @ktosopl根本原因是該類*不應*稱為* Impl,但我知道您無論如何一直在拖釣我們
—彼得·科夫勒(@codecopkofler) 2014年10月9日
我們走吧 !
翻譯自: https://www.javacodegeeks.com/2014/10/the-dreaded-defaultabstracthelperimpl.html
總結
以上是生活随笔為你收集整理的可怕的DefaultAbstractHelperImpl的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ps3e3怎么切换模式(ps33d模式)
- 下一篇: 在JBoss Fuse / Fabric