在我的一个项目中,我有两个包含 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 创建构建器。所以对于上面的代码,aFooBuilder
是自动生成的,我可以这样使用:
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 源代码)。我的问题是:如何在不引入任何常见依赖项(项目和插件之间)的情况下处理上述两个问题:
<Question>
我需要一个 Defaults 类(或类似机制)来获取生成的单元测试的默认值(这是概念的关键部分,如果没有完全测试,我不会信任自动生成的构建器)。请帮我想出一个好的通用方法来解决这个问题,因为每个项目都有自己的域对象。
我需要一种将泛型类型与构建器生成器通信的通用方式。我正在使用的当前基于注释的版本并不令人满意,因为项目和插件都需要了解相同的注释。
</Question>
有任何想法吗?
顺便说一句:我知道使用构建器的真正关键点是使对象不可变。我不能让我的不可变,因为标准的 java bean 是必需的,但是我使用 AspectJ 来强制在我的代码库中的任何地方都没有调用 set-methods 和构造函数,除了在构建器中,因此出于实际目的,生成的对象是不可变的.
另外:是的,我知道现有的 Builder-generator IDE 插件。这不符合我的目的,我想要一个自动化的解决方案,只要底层代码发生变化,它总是最新的。
Matt B 请求了一些关于我如何生成我的构建器的信息。这就是我所做的:
我每次反射读取一个类,用于Introspector.getBeanInfo(clazz).getPropertyDescriptors()
获取属性描述符数组。我所有的构建器都有一个基类AbstractBuilder<T>
,在上述情况下T
。Foo
这是Abstract Builder 类的代码。对于PropertyDescriptor
数组中的每个属性,都会生成一个带有属性名称的方法。这将是FooBuilder.bar(String)
:
public FooBuilder bar(String bar){
setProperty("bar", bar);
return this;
}
中的build()
方法AbstractBuilder
实例化对象并分配其属性映射中的所有属性。