自动处理重复的代码
在本文中,我將描述如何使用Java :: Geci生成器Repeated以克服泛型不能為原始類型的Java語言不足。 該示例是對Apache Commons Lang庫的建議擴展。
介紹
當您復制粘貼代碼時,您做錯了什么。 至少那是感知。 您必須創建更通用的代碼結構,以便可以多次使用不同的參數而不是類似的代碼。
這并非總是如此。 有時您必須重復一些代碼,因為您使用的語言(尚)不支持該問題所需的功能。
這太抽象了。 讓我們看一個特定的示例,以及如何使用在Java :: Geci框架中運行的Repeated源生成器來管理它。
問題
Apache Commons Lang庫中的org.apache.commons.lang3.Functions類定義了一個內部接口FailableFunction 。 這是一個通用接口,定義為
@FunctionalInterface public interface FailableFunction<I, O, T extends Throwable> { /** * Apply the function. * @param pInput the input for the function * @return the result of the function * @throws T if the function fails */ O apply(I pInput) throws T; }這本質上與Function<I,O> ,后者將I轉換為O但是由于接口是有故障的,因此它也可能引發類型T的異常。
新的需要是
public interface Failable<I>Function<O, T extends Throwable>每個<I>原語值的面。 問題是泛型在Java中還不能是原始的,因此我們應該為每種原始類型分離接口,例如
@FunctionalInterface public interface FailableCharFunction<O, T extends Throwable> { O apply( char pInput) throws T; } @FunctionalInterface public interface FailableByteFunction<O, T extends Throwable> { O apply( byte pInput) throws T; } @FunctionalInterface public interface FailableShortFunction<O, T extends Throwable> { O apply( short pInput) throws T; } @FunctionalInterface public interface FailableIntFunction<O, T extends Throwable> { O apply( int pInput) throws T; } ... and so on ...這是許多非常相似的方法,可以很容易地由模板描述,然后由某些代碼生成工具生成。
使用Java :: Geci進行模板處理
Java :: Geci框架附帶了許多現成的生成器。 其中之一是功能強大的Repeated生成器,正是出于這個目的。 如果必須重復使用可能的參數的代碼,則可以定義模板,值和Repeated將生成解析模板參數的代碼。
向POM添加依賴項
我們要做的第一件事是將Java :: Geci依賴項添加到pom.xml文件中。 由于Apache Commons Language仍基于Java 8,因此我們必須使用Java :: Geci 1.2.0的Java 8反向端口:
< dependency > < groupId >com.javax1.geci</ groupId > < artifactId >javageci-core</ artifactId > < version >1.2.0</ version > < scope >test</ scope > </ dependency >注意,依賴項的范圍是test 。 Repeated生成器可以方便地使用,而字節碼中不會保留任何Geci注釋,因此它們是編譯時的依賴項。 實際上,所有生成器都可以使用而無需注釋,因此不需要任何編譯依賴關系,而這對于生產來說是額外的依賴關系。 在Repeated的情況下,這甚至很容易做到。
運行發電機的單元測試
我們要做的第二件事是創建一個將執行生成器的單元測試。 Java :: Geci生成器在單元測試階段運行,因此它們可以使用反射以及實際的源代碼訪問已編譯的代碼。 如果生成的任何代碼與源文件中已有的代碼不同,則測試將失敗,并且應再次執行生成過程。 由于發生器是(應該)冪等的,因此測試第二次不應失敗。
以我的經驗,很遺憾,此工作流程會影響開發人員的行為。 運行測試/失敗,再次運行! 這是一個糟糕的循環。 有時我碰巧發現自己不是在代碼生成器發生故障的情況下重新執行單元測試。 但是,這就是Java :: Geci的工作方式。
有一些關于Java :: Geci工作流程的文章
- 您的代碼是多余的,請繼續使用!
- 保持JavaDoc為最新
- 將對象轉換為映射并返回
- 反射選擇器表達式
- 使用Java :: Geci生成Getter和Setters
- 創建一個Java :: Geci生成器
- 如何生成源代碼
因此,在此我不會重復總體架構及其工作流程。
單元測試如下:
@Test void generatePrimitiveFailables() throws Exception { final Geci geci = new Geci(); Assertions.assertFalse(geci.source(Source.maven().mainSource()) .only( "Functions" ) .register(Repeated.builder() .values( "char,byte,short,int,long,float,double,boolean" ) .selector( "repeated" ) .define((ctx, s) -> ctx.segment().param( "Value" , CaseTools.ucase(s))) .build()) .generate(), geci.failed()); }調用source() , register()和only()配置框架。 此配置告訴框架使用項目主Java src目錄中的源文件,并僅使用文件名"Functions" 。 在我們調用generate()開始代碼生成之前,對register()的調用會register() Repeated生成器實例。
生成器實例本身是使用內置的生成器創建的,該生成器使我們可以配置生成器。 在這種情況下,對values()的調用定義了我們要重復使用模板的逗號分隔的值列表(稍后在注釋中的代碼中定義)。 對selector()的調用定義了此代碼重復代碼的標識符。 單個源文件可能包含多個模板。 每個模板可以使用不同的值列表進行處理,結果將插入到不同的輸出段中,并插入源文件中。 在這種情況下,仍然只有一個這樣的代碼生成模板,它必須用一個名稱標識,并且該名稱也必須在editor-fold部分中使用,該部分是生成代碼的占位符。
實際使用生成器的名稱有兩個作用。 其一是它標識了編輯器折疊段和模板。 另一個是框架將看到帶有該標識符的編輯器折疊段,并且它將識別出該源文件需要該生成器的注意。 另一種可能性是在類中添加@Repeated或@Geci("repeated")批注。
如果標識符是別的東西并且不repeated那么生成器“ Repeated將不會觸及源代碼,或者我們需要將另一個段標識為“ repeated ,除了觸發生成器之外,它實際上不會被使用。
對define()的調用define()了一個BiConsumer ,該BiConsumer獲取了上下文引用和實際值。 在這種情況下, BiConsumer計算大寫值,并將其放入與名稱Value相關聯的實際細分參數集中。 默認情況下,實際值與名稱value關聯,并且傳遞給方法define()的BiConsumer可以定義和注冊其他參數。 在這種情況下,它將添加新值
value Value char --> Char byte --> Byte short --> Short int --> Int long --> Long float --> Float double --> Double boolean --> Boolean源代碼
第三件事是在源文件中準備模板和輸出段。
輸出段的準備非常簡單。 它只是一個編輯器折疊:
//<editor-fold id="repeated"> //</editor-fold>生成的代碼將自動插入兩行之間,并且編輯器(Eclipse,IntelliJ或NetBeans)將允許您關閉折疊。 您不想編輯此代碼:它已生成。
該模板將如下所示:
/* TEMPLATE repeated @FunctionalInterface public interface Failable{{Value}}Function<O, T extends Throwable> { O apply({{value}} pInput) throws T; } */代碼生成器找到模板的開始,以查找與/* TEMPLATE name格式匹配的行,并收集連續的行,直到注釋的結尾。
該模板使用小胡子模板的占位符格式,即用雙括號括起來的值的名稱。 雙括號在Java中很少見。
當我們運行單元測試時,它將生成我在本文開頭已經列出的代碼。 (然后,它當然會失敗:修改了源代碼,然后再次編譯。)
摘要和總結
最重要的要點和警告:源代碼生成是旨在彌補編程語言不足的一種工具。 不要使用代碼生成來修正語言的不足,而不僅僅是語言的不足,技能或知識的不足。 生成代碼的簡單方法不是生成不必要的冗余代碼的借口。
另一個收獲是,在Java中使用此生成器非常容易。 該功能可以與Java所沒有的C預處理器相媲美,而且效果良好。 需要時使用它。 即使依賴項的設置和單元測試的開銷可能很小,但可維護性通常會償還此成本。
翻譯自: https://www.javacodegeeks.com/2019/09/handling-repeated-code-automatically.html
總結
- 上一篇: unwind neo4j_Neo4j 2
- 下一篇: 剑魂安卓下载(剑魂安卓)