如何在Java 8中实现构建器模式? [英] How to implement the builder pattern in Java 8?

查看:120
本文介绍了如何在Java 8中实现构建器模式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通常我觉得用java-8之前的设置来实现构建器模式是一件很麻烦的事情。总是有很多重复的代码。实际上代码重复检测器可能被认为是样板。



>,这几乎可以考虑用java-8之前的设施制作的构建器的每个方法与其他方法重复。



所以考虑下面的类,它是pre -java-8构建器:

  public class Person {

private String name;
private int age;

public String getName(){
return name;
}

public void setName(String name){
this.name = name;
}

public int getAge(){
return age;
}

public void setAge(int age){
this.age = age;
}
}

public class PersonBuilder {

private static class PersonState {
public String name;
public int age;
}

private PersonState state = new PersonState();

public PersonBuilder withName(String name){
state.name = name;
返回这个;
}

public PersonBuilder withAge(int age){
state.age = age;
返回这个;
}

public Person build(){
Person person = new Person();
person.setAge(state.age);
person.setName(state.name);
state = new PersonState();
退货人;
}
}

如何使用java-8实现构建器模式设备?

解决方案

GenericBuilder



构建可变对象(不可变对象稍后解决)的想法是使用应该构建的实例的setter的方法引用。这使我们成为一个通用构建器,它能够使用默认构造函数构建每个POJO - 一个构建器来统一它们; - )



实现是这样的: p>

  public class GenericBuilder< T> {

私人最终供应商< T>实例化;

private List< Consumer< T>> instanceModifiers = new ArrayList<>();

public GenericBuilder(供应商< T>实例化程序){
this.instantiator = instantiator;
}

public static< T> GenericBuilder< T>的(供应商< T>实例化器){
返回新的GenericBuilder< T>(实例化);
}

public< U> GenericBuilder< T>与(BiConsumer< T,U>消费者,U值){
Consumer< T> c = instance - > consumer.accept(instance,value);
instanceModifiers.add(c);
返回这个;
}

public T build(){
T value = instantiator.get();
instanceModifiers.forEach(修饰符 - > modifier.accept(value));
instanceModifiers.clear()
返回值;
}
}

构建器由构建新的实例的供应商构建然后这些实例由方法指定的修改修改。



GenericBuilder 将用于 Person ,如下所示:

  Person value = GenericBuilder.of(Person :: new)
.with(Person :: setName,Otto)。with(Person :: setAge,5).build();



属性和其他用途



但是更多关于该建设者发现。



例如,上述实现会清除修饰符。这可以移动到自己的方法中。因此,构建器将在修改之间保持其状态,并且容易创建多个相等的实例。或者,根据 instanceModifier 的性质,列出不同的对象。例如,一个 instanceModifier 可以从增加的计数器读取它的值。



继续这个想法,我们可以实现一个 fork 方法将返回一个新的克隆,它被调用的 GenericBuilder 实例。这很容易,因为构建器的状态只是实例化器 instanceModifiers 的列表。从那里开始,两个建立者都可以使用其他一些 instanceModifiers 进行更改。他们将分享相同的基础,并在建立的实例上设置一些额外的状态。



最后一点我认为特别有用的时候,需要重型实体进行单元甚至集成测试在企业应用。对于实体,对于实体而言,不会有上帝对象,而是建设者。



GenericBuilder 也可以替代需求为不同的测试值工厂。在我目前的项目中,有许多工厂用于创建测试实例。该代码紧密耦合到不同的测试场景,并且难以在稍微不同的情况下提取测试工厂的部分以便在另一个测试工厂中重复使用。使用 GenericBuilder ,重复使用变得容易得多,因为只有 instanceModifiers 的特定列表。



要验证创建的实例是否有效,可以使用一组谓词来初始化 GenericBuilder ,这些谓词在 build 方法,所有 instanceModifiers 运行。

 code> public T build(){
T value = instantiator.get();
instanceModifiers.forEach(修饰符 - > modifier.accept(value));
verifyPredicates(value);
instanceModifiers.clear();
返回值;
}

private void verifyPredicates(T value){
列表<谓词< T>> violated = predicates.stream()
.filter(e - >!e.test(value))。collect(Collectors.toList());
if(!violated.isEmpty()){
throw new IllegalStateException(value.toString()
+违反谓词+违反);
}
}



创建不变的对象



要使用上述方案创建不可变对象,请将不可变对象的状态提取到可变对象中,并使用实例化器和构建器对可变状态对象进行操作。然后,添加一个将为可变状态创建一个新的不可变实例的函数。但是,这要求不可变对象的状态如此封装,或者以这种方式更改(基本上将参数对象模式应用于其构造函数)。



在某种程度上不同于建造者在java-8之前被使用。在那里,构建器本身是可变对象,在最后创建了一个新的实例。现在,我们分离了构建器保存在可变对象中的状态和构建器功能本身。



本质上

停止编写样板构建器模式,并使用 GenericBuilder 获得高效。


Often I find it tedious to implement the builder pattern with pre-java-8 setups. There is always lots of nearly duplicated code. The builder itself could be considered boilerplate.

In fact there are code duplicate detectors, that would consider nearly each method of a builder made with pre-java-8 facilities as a duplicate of every other method.

So considering the following class and it's pre-java-8 builder:

public class Person {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class PersonBuilder {

    private static class PersonState {
        public String name;
        public int age;
    }

    private PersonState  state = new PersonState();

    public PersonBuilder withName(String name) {
        state.name = name;
        return this;
    }

    public PersonBuilder withAge(int age) {
        state.age = age;
        return this;
    }

    public Person build() {
        Person person = new Person();
        person.setAge(state.age);
        person.setName(state.name);
        state = new PersonState();
        return person;
    }
}

How should the builder pattern be implemented using java-8 facilities?

解决方案

The GenericBuilder

The idea for building mutable objects (immutable objects are addressed later on) is to use method references to setters of the instance that should be built. This leads us to a generic builder that is capable of building every POJO with a default constructor - one builder to rule them all ;-)

The implementation is this:

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;
    }
}

The builder is constructed with a supplier that creates new instances and then those instances are modified by the modifications specified with the with method.

The GenericBuilder would be used for Person like this:

Person value = GenericBuilder.of(Person::new)
            .with(Person::setName, "Otto").with(Person::setAge, 5).build();

Properties and further Usages

But there is more about that builder to discover.

For example, the above implementation clears the modifiers. This could be moved into its own method. Therefore, the builder would keep its state between modifications and it would be easy create multiple equal instances. Or, depending on the nature of an instanceModifier, a list of varying objects. For example, an instanceModifier could read its value from an increasing counter.

Continuing with this thought, we could implement a fork method that would return a new clone of the GenericBuilder instance that it is called on. This is easily possible because the state of the builder is just the instantiator and the list of instanceModifiers. From there on, both builders could be altered with some other instanceModifiers. They would share the same basis and have some additional state set on built instances.

The last point I consider especially helpful when needing heavy entities for unit or even integration tests in enterprise applications. There would be no god-object for entities, but for builders instead.

The GenericBuilder can also replace the need for different test value factories. In my current project, there are many factories used for creating test instances. The code is tightly coupled to different test scenarios and it is difficult to extract portions of a test factory for reuse in another test factory in a slightly different scenario. With the GenericBuilder, reusing this becomes much easier as there is only a specific list of instanceModifiers.

To verify that created instances are valid, the GenericBuilder could be initialized with a set of predicates, which are verified in the build method after all instanceModifiers are run.

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);
    }
}

Immutable object creation

To use the above scheme for the creation of immutable objects, extract the state of the immutable object into a mutable object and use the instantiator and builder to operate on the mutable state object. Then, add a function that will create a new immutable instance for the mutable state. However, this requires that the immutable object either has its state encapsulated like this or it be changed in that fashion (basically applying parameter object pattern to its constructor).

This is in some way different than a builder was used in pre-java-8 times. There, the builder itself was the mutable object that created a new instance at the end. Now, we have a separation of the state a builder keeps in a mutable object and the builder functionality itself.

In essence
Stop writing boilerplate builder patterns and get productive using the GenericBuilder.

这篇关于如何在Java 8中实现构建器模式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆