Java Builder生成器问题

Sea*_*oyd 8 java code-generation builder-pattern

在我的一个项目中,我有两个包含DTO的包,POJO只有吸气剂和二传手.虽然重要的是它们是简单的Java bean(例如,因为Apache CXF使用它们来创建Web服务XSD等),但它也很糟糕且容易出错.

Foo foo = new Foo();
foo.setBar("baz");
foo.setPhleem(123);
return foo;
Run Code Online (Sandbox Code Playgroud)

我更喜欢流畅的接口和构建器对象,因此我使用maven/gmaven自动为DTO创建构建器.所以对于上面的代码,FooBuilder会自动生成一个,我可以像这样使用:

Foo foo = new FooBuilder()
           .bar("baz")
           .phleem(123)
           .build();
Run Code Online (Sandbox Code Playgroud)

我还自动为生成的Builders生成单元测试.单元测试将生成上述两个代码(构建器版本和非构建器版本),并声明两个版本在equals()和的方面都是等效的hashcode().我可以实现的方法是拥有一个全局可访问的Map,其中包含每种属性类型的默认值.像这样的东西:

public final class Defaults{
    private Defaults(){}
    private static final Map<Class<?>, Object> DEFAULT_VALUES =
        new HashMap<Class<?>, Object>();
    static{
        DEFAULT_VALUES.put(String.class, "baz");
        // argh, autoboxing is necessary :-)
        DEFAULT_VALUES.put(int.class, 123);
        // etc. etc.
    }
    public static getPropertyValue(Class<?> type){
        return DEFAULT_VALUES.get(type);
    }
}
Run Code Online (Sandbox Code Playgroud)

另一个非平凡的方面是pojos有时会有收集成员.例如:

foo.setBings(List<Bing> bings)
Run Code Online (Sandbox Code Playgroud)

但在我的构建器中,我希望从这种情况生成两种方法:set方法和add方法:

fooBuilder.bings(List<Bing> bings); // set method
fooBuilder.addBing(Bing bing); // add method
Run Code Online (Sandbox Code Playgroud)

我通过在属性字段中添加自定义注释来解决这个问题 Foo

@ComponentType(Bing.class)
private List<Bing> bings;
Run Code Online (Sandbox Code Playgroud)

构建器构建器(sic)读取注释并将该值用作要生成的方法的泛型类型.

我们现在越来越接近这个问题(对不起,简洁不是我的优点之一:-)).

我已经意识到这个构建器方法可以在多个项目中使用,所以我想把它变成一个maven插件.我非常清楚如何生成maven插件,因此这不是问题的一部分(也不是如何生成有效的Java源代码).我的问题是:如何在不引入任何公共依赖项的情况下处理上述两个问题(在Project和Plugin之间):

<Question>

  1. 我需要一个Defaults类(或类似的机制)来获取生成的单元测试的默认值(这是概念的关键部分,如果没有经过全面测试,我不相信自动生成的构建器).鉴于每个项目都有自己的域对象,请帮我提出一个很好的通用方法来解决这个问题.

  2. 我需要一种将通用类型传递给构建器生成器的常用方法.我正在使用的当前基于注释的版本并不令人满意,因为项目和插件都需要知道相同的注释.

</Question>

有任何想法吗?

顺便说一句:我知道使用构建器的真正关键点是使对象不可变.我不能让我的不可变,因为标准的java bean是必需的,但我使用AspectJ强制在我的代码库中除了构建器之外的任何地方都没有调用set-methods和构造函数,所以出于实际目的,结果对象是不可变的.

另外:是的,我知道现有的Builder-generator IDE插件.这不符合我的目的,我想要一个自动化的解决方案,每当底层代码发生变化时,它始终是最新的.


Matt B要求我提供一些关于如何生成构建器的信息.这是我做的:

我按照反射读取了一个类,用于Introspector.getBeanInfo(clazz).getPropertyDescriptors()获取属性描述符数组.我所有的建设者有一个基类AbstractBuilder<T>,其中TFoo在上述情况下.这是Abstract Builder类的代码.对于PropertyDescriptor数组中的每个属性,都会生成一个带有属性名称的方法.这将是以下的实施FooBuilder.bar(String):

public FooBuilder bar(String bar){
    setProperty("bar", bar);
    return this;
}
Run Code Online (Sandbox Code Playgroud)

build()方法AbstractBuilder实例化对象并在其属性映射中分配所有属性.

小智 1

你看过迪泽尔吗?这是一个生成器生成器。

  1. 它处理泛型类型,因此对于问题 2 可能会有所帮助
  2. 它生成所有接口,并基于描述 XML 文件实现样板。您也许能够通过自省来生成此 XML(或者甚至直接进入较低的 API)
  3. 它被捆绑为 Maven 插件。