GenericBuilder
构建 可变对象
(稍后将讨论不可变对象)的想法是使用方法引用对应该构建的实例的设置方法进行设置。这使我们得到了一个通用的构建器,该构建器能够使用默认的构建器构建每个POJO-
一个构建器将它们全部统治;-)
实现是这样的:
public class GenericBuilder<T> { private final Supplier<T> instantiator; private List<Consumer<T>> instanceModifiers = new ArrayList<>(); public GenericBuilder(Supplier<T> instantiator) { this.instantiator = instantiator; } public static <T> GenericBuilder<T> of(Supplier<T> instantiator) { return new GenericBuilder<T>(instantiator); } public <U> GenericBuilder<T> with(BiConsumer<T, U> consumer, U value) { Consumer<T> c = instance -> consumer.accept(instance, value); instanceModifiers.add(c); return this; } public T build() { T value = instantiator.get(); instanceModifiers.forEach(modifier -> modifier.accept(value)); instanceModifiers.clear(); return value; }}构建器由创建新实例的供应商构造,然后通过
with方法指定的修改来修改这些实例。
在
GenericBuilder将用于
Person如下:
Person value = GenericBuilder.of(Person::new) .with(Person::setName, "Otto").with(Person::setAge, 5).build();
属性和进一步用途
但是,还有更多关于该构建器的发现。
例如,以上实现清除了修饰符。可以将其移动到自己的方法中。因此,构建器将在修改之间保持其状态,并且很容易创建多个相等的实例。或者,根据的性质
instanceModifier,列出不同的对象。例如,
instanceModifier可以从递增的计数器中读取其值。
继续这种想法,我们可以实现一个
fork方法,该方法将返回
GenericBuilder调用它的实例的新克隆。这很容易实现,因为构建器的状态仅为
instantiator和的列表
instanceModifiers。从那以后,两个建造者都可以与其他建造者一起改变
instanceModifiers。它们将共享相同的基础,并在已构建的实例上设置一些其他状态。
当需要在企业应用程序中进行大型实体进行单元甚至集成测试时,我认为最后一点特别有用。对于实体,没有上帝的对象,但是对于建造者。
该
GenericBuilder还可以取代不同的测试值工厂的需要。在我当前的项目中,有许多工厂用于创建测试实例。该代码与不同的测试场景紧密耦合,并且很难提取测试工厂的一部分以在稍有不同的场景中在另一个测试工厂中重用。使用时
GenericBuilder,由于只存在一个特定的列表,因此重新使用它变得容易得多
instanceModifiers。
为了验证创建的实例是否有效,
GenericBuilder可以使用一组谓词来初始化,这些谓词将
build在所有
instanceModifiers方法运行后在方法中进行验证。
public T build() { T value = instantiator.get(); instanceModifiers.forEach(modifier -> modifier.accept(value)); verifyPredicates(value); instanceModifiers.clear(); return value;}private void verifyPredicates(T value) { List<Predicate<T>> violated = predicates.stream() .filter(e -> !e.test(value)).collect(Collectors.toList()); if (!violated.isEmpty()) { throw new IllegalStateException(value.toString() + " violates predicates " + violated); }}不变的对象创建
要使用上述方案创建 不可变对象
,请将不可变对象的状态提取到可变对象中,并使用实例化器和生成器对可变状态对象进行操作。然后,添加一个函数,该函数将为可变状态创建一个新的不可变实例。但是,这要求不可变对象要么像这样封装其状态,要么以这种方式更改(基本上将参数对象模式应用于其构造函数)。
这与Java-8之前的版本中使用的生成器有所不同。在那里,构建器本身就是可变对象,它最终创建了一个新实例。现在,我们将构建器保留在可变对象中的状态与构建器功能本身分开。
本质上,
停止编写样板构建器模式,并使用可以提高生产率
GenericBuilder。



