Java Fluent builder设计模式-如何强制客户设置变量

Java Fluent builder设计模式-如何强制客户设置变量,java,design-patterns,builder,Java,Design Patterns,Builder,我对设计模式有问题。假设我有一个简单的类: public class Person { private String name; private String surname; private int age; private Person(){} public String toString() { return "name: "+name+" surname: "+surname+&

我对设计模式有问题。假设我有一个简单的类:

public class Person {

    private String name;
    private String surname;
    private int age;

    private Person(){}

    public String toString()
    {
        return "name: "+name+" surname: "+surname+" age: "+age;
    }

    public static final class Builder{

        private String name;
        private String surname;
        private int age;

        public Builder name (String name){
            this.name = name;
            return this;
        }

        public Builder surname (String surname){
            this.surname = surname;
            return this;
        }

        public Builder age (int age){
            this.age = age;
            return this;
        }

        public Person build(){
            Person person = new Person();
            person.name=name;
            person.surname=surname;
            person.age=age;
            return person;
        }
    }
}

它工作正常,但在调用“build”方法之前不需要设置变量。我怎样才能改变这一点呢?

一种简单的强制方法是,如果没有设置所有设置,则在
build()
中抛出一个异常。连同文档,这可能已经足够了,但它不会提供任何编译时检查

另一种选择是,如果您对强制执行某些命令没有意见,可以使用“步骤”对象:

然后,使用情况需要如下所示:

Person p = Builder.name("John").surname("Smith").age(42).build();
姓名、姓氏和年龄都需要指定,而且在
build()
可用之前都需要按此顺序指定

如果所有字段都是必填字段,您还可以删除
build()
,创建
Person
已在
age(int)

还请注意,您可以直接将步骤放入
Person
中,而不是将步骤放入
Builder
中。静态“init”方法
name(String)
已经创建了一个
Person
实例,但它只有在调用
age(int)
之后才能访问:


一个简单的方法是在
build()
中抛出一个异常,如果没有设置所有设置。连同文档,这可能已经足够了,但它不会提供任何编译时检查

另一种选择是,如果您对强制执行某些命令没有意见,可以使用“步骤”对象:

然后,使用情况需要如下所示:

Person p = Builder.name("John").surname("Smith").age(42).build();
姓名、姓氏和年龄都需要指定,而且在
build()
可用之前都需要按此顺序指定

如果所有字段都是必填字段,您还可以删除
build()
,创建
Person
已在
age(int)

还请注意,您可以直接将步骤放入
Person
中,而不是将步骤放入
Builder
中。静态“init”方法
name(String)
已经创建了一个
Person
实例,但它只有在调用
age(int)
之后才能访问:


您可以通过做以下几件事来改进此代码:

  • 创建接受所有3个值的Person的非默认构造函数
  • 公共类人物{
    公众人物(字符串第一、字符串最后、整数){
    this.first=first;
    this.last=last;
    这个。年龄=年龄;
    }
    }
    
  • 添加此构造函数中参数的验证抛出IllegalArgumentException
  • if(年龄<0岁){
    抛出新的IllegalArgumentException(“年龄不能为负:+Age”);
    }
    if(first==null&&last==null){
    抛出新的IllegalArgumentException(“名字或姓氏为空”);
    }
    
  • (可选,用于单元测试)您还可以在生成器中初始化默认值,以便在静态工厂方法和非默认构造函数中进行测试:
  • publicstaticbuilder someAdultPerson(){
    归还新建筑商(“埃里卡”、“穆斯特曼”,18岁);
    }
    
  • 使用此接口在测试中支持BDD表示法:
  • 接口生成器{
    T build();
    静态给定(生成器){return Builder.build();}
    }
    ... 
    班主任{
    类生成器实现my.example.Builder{…}
    }
    ...
    导入static Person.Builder.someAdultPerson;
    ...
    Person=给定的(某个成年人)年龄(27岁);
    
    您可以通过做以下几件事来改进此代码:

  • 创建接受所有3个值的Person的非默认构造函数
  • 公共类人物{
    公众人物(字符串第一、字符串最后、整数){
    this.first=first;
    this.last=last;
    这个。年龄=年龄;
    }
    }
    
  • 添加此构造函数中参数的验证抛出IllegalArgumentException
  • if(年龄<0岁){
    抛出新的IllegalArgumentException(“年龄不能为负:+Age”);
    }
    if(first==null&&last==null){
    抛出新的IllegalArgumentException(“名字或姓氏为空”);
    }
    
  • (可选,用于单元测试)您还可以在生成器中初始化默认值,以便在静态工厂方法和非默认构造函数中进行测试:
  • publicstaticbuilder someAdultPerson(){
    归还新建筑商(“埃里卡”、“穆斯特曼”,18岁);
    }
    
  • 使用此接口在测试中支持BDD表示法:
  • 接口生成器{
    T build();
    静态给定(生成器){return Builder.build();}
    }
    ... 
    班主任{
    类生成器实现my.example.Builder{…}
    }
    ...
    导入static Person.Builder.someAdultPerson;
    ...
    Person=给定的(某个成年人)年龄(27岁);
    
    在用户可以调用
    build()
    @BoristheSpider之前,您可以使用基于lambda的构建器强制执行一系列方法调用,因为构建器模式有很多不同的版本。你介意链接到你所指的那个吗?在用户调用
    build()
    @borisspider之前,你可以使用基于lambda的构建器强制执行一系列方法调用,因为构建器模式有很多不同的版本。你介意链接到你指的那个吗?
    //expanded to make it more obvious: the steps don't provide access to the Person until all have been executed
    SurnameStep surnameStep = Person.name("John");
    AgeStep ageStep = surnameStep .surname("Smith");
    Person johnSmith = ageStep.age(42);