【问题标题】:Java Builder generator problemJava Builder 生成器问题
【发布时间】:2010-11-23 14:33:09
【问题描述】:

在我的一个项目中,我有两个包含 DTO、POJO 的包,其中只有 getter 和 setter。虽然它们是简单的 java bean 很重要(例如,因为 Apache CXF 使用它们来创建 Web 服务 XSD 等),但这样的编程也很糟糕且容易出错。

Foo foo = new Foo();
foo.setBar("baz");
foo.setPhleem(123);
return foo;

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

Foo foo = new FooBuilder()
           .bar("baz")
           .phleem(123)
           .build();

我还为生成的构建器自动生成单元测试。单元测试将生成上述两个代码(构建器版本和非构建器版本)并断言两个版本在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);
    }
}

另一个重要的方面是 pojo 有时具有集合成员。例如:

foo.setBings(List<Bing> bings)

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

fooBuilder.bings(List<Bing> bings); // set method
fooBuilder.addBing(Bing bing); // add method

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

@ComponentType(Bing.class)
private List<Bing> bings;

builder builder (sic) 读取注解并使用该值作为要生成的方法的泛型类型。

我们现在离问题越来越近了(抱歉,简洁不是我的优点之一 :-))。

我意识到这种构建器方法可以用于多个项目,所以我正在考虑将它变成一个 maven 插件。我非常清楚如何生成一个 Maven 插件,所以这不是问题的一部分(也不是如何生成有效的 Java 源代码)。我的问题是:如何在不引入任何常见依赖项(项目和插件之间)的情况下处理上述两个问题:

&lt;Question&gt;

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

  2. 我需要一种将泛型类型与构建器生成器通信的通用方式。我当前使用的基于注解的版本并不令人满意,因为项目和插件都需要知道相同的注解。

&lt;/Question&gt;

有什么想法吗?

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

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


Matt B 要求提供一些有关我如何生成构建器的信息。这是我的工作:

我每次反射读取一个类,使用Introspector.getBeanInfo(clazz).getPropertyDescriptors() 获取属性描述符数组。我所有的构建器都有一个基类AbstractBuilder&lt;T&gt;,在上述情况下,T 将是Foo。这是the code of the Abstract Builder class。对于PropertyDescriptor 数组中的每个属性,都会生成一个带有属性名称的方法。这将是FooBuilder.bar(String) 的实现:

public FooBuilder bar(String bar){
    setProperty("bar", bar);
    return this;
}

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

【问题讨论】:

  • 旁白:当我需要生成Java代码(尤其是大量的单元测试)时,我使用grep/sed+python...
  • 我喜欢使用能理解语言的技术。这将其限制为 a) 反射 b) 源代码解析器 c) 字节码工具,如 ASM
  • 出于好奇,什么 Maven 插件会为您生成 Builder 类?
  • @matt b:还没有。我正在考虑写一个。我有我使用 GMaven 从 maven 调用的工作 groovy 代码,如果不是因为我提到的两个问题,将这个机制变成一个 maven 插件将是微不足道的。
  • 我说的现有插件是一个eclipse插件,不是maven插件

标签: java code-generation builder-pattern


【解决方案1】:

POJO 是一个不遵循 Java Bean 规范的对象。 IE。它没有设置器/获取器。

JavaBeans 不需要有setter,如果你不想调用它们,就不要生成它们。 (您的构建器可以调用包本地或私有构造函数来创建您的不可变对象)

【讨论】:

  • 好的,所以它们是 bean,而不是 POJO。但他们确实需要 setter,因为我们使用的几个外部工具依赖于 setter 的存在(即使我们自己不使用它们)。您正在回答一个不同的问题(我并不是说您的答案无效,只是与我无关)。问题不是关于不变性,而是关于两个具体问题,您的回答都没有解决这些问题。
【解决方案2】:

你看过Diezel 吗? 它是一个 Builder 生成器。

  1. 它处理泛型类型,因此在这里可能对问题 2 有所帮助
  2. 它根据描述 XML 文件生成所有接口和实现样板。您也许可以通过自省来生成这个 XML(甚至直接进入较低的 API)
  3. 它被捆绑为一个 maven 插件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-15
    • 2021-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多