自动处理重复的代码

在本文中,我将描述如何使用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 > 

注意,依赖项的范围是testRepeated生成器可以方便地使用,而字节码中不会保留任何Geci注释,因此它们是编译时的依赖项。 实际上,所有生成器都可以使用而无需注释,因此不需要任何编译依赖关系,而这对于生产来说是额外的依赖关系。 在Repeated的情况下,这甚至很容易做到。

运行发电机的单元测试

我们要做的第二件事是创建一个将执行生成器的单元测试。 Java :: Geci生成器在单元测试阶段运行,因此它们可以使用反射以及实际的源代码访问已编译的代码。 如果生成的任何代码与源文件中已有的代码不同,则测试将失败,并且应再次执行生成过程。 由于发生器是(应该)幂等的,因此测试第二次不应失败。

以我的经验,很遗憾,此工作流程会影响开发人员的行为。 运行测试/失败,再次运行! 这是一个糟糕的循环。 有时我碰巧发现自己不是在代码生成器发生故障的情况下重新执行单元测试。 但是,这就是Java :: Geci的工作方式。

有一些关于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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值