在寻找Builder模式的解释时,您可能会发现一些文章都展示了专门用于创建对象的类。 这很简单,我们可以进一步研究这种模式。
让我们从上课开始人下面:
public class Person {
private String firstname;
private String lastname;
private String nickname;
private String email;
private String gender;
private String country;
private String city;
public Person(String firstname, String lastname, String nickname, String email, String gender, String country, String city) {
this.firstname = firstname;
this.lastname = lastname;
this.nickname = nickname;
this.email = email;
this.gender = gender;
this.country = country;
this.city = city;
}
}
从这段代码中我们可以看到,创建新的Person对象时会出现问题:
- constructor that's too big,
- constructor parameters have all the same type. We have to remember the right order of each parameter when using it. In fact, we are façing a common code smell: primitive obsession. You can take a look at this article which explains how to recognize and deal with them.
- How can we set up constraints? It can be difficult to check nullity or to ensure consistency between fields.
因此,为了处理其中的一些问题,我们可以添加几个构造函数。 例如:
public Person(String firstname, String lastname) {
this(firstname, lastname, null, null, null, null, null);
}
public Person(String firstname, String lastname, String nickname) {
this(firstname, lastname, nickname, null, null, null, null);
}
public Person(String firstname, String lastname, String nickname, String email) {
this(firstname, lastname, nickname, email, null, null, null);
}
通过向我们的Person类添加不同的构造函数,结果是建设责任委托给客户。 想要创建对象Person的开发人员必须知道要使用哪个构造函数以及原因。 而且,生成的代码难以阅读和理解:
Person john = new Person("john", "smith", "john");
在这段简单的代码中,很难知道名字,姓氏和昵称参数在哪里。 我们必须遍历源代码以检查将初始化哪些字段。 一个方便的解决方案是创建一个构建器类来解决此可读性问题。
public class PersonBuilder {
private final Person person;
private PersonBuilder() {
person = new Person();
}
public static PersonBuilder builder() {
return new PersonBuilder();
}
public PersonBuilder withFirstname(String firstname) {
if (firstname == null) {
throw new IllegalArgumentException("firstname must be not null");
}
person.setFirstname(firstname);
return this;
}
public PersonBuilder withLastname(String lastname) {
if (lastname == null) {
throw new IllegalArgumentException("lastname must be not null");
}
person.setLastname(lastname);
return this;
}
...
public Person build() {
return person;
}
}
然后,我们可以像这样使用它:
Person john = PersonBuilder.builder()
.withFirstname("john")
.withLastname("smith")
.build();
Great! This solution has the benefit of explicit arguments. We can easily understand what person's first name is. In addition, it was easy to add a not-null constraint for each building method. Furthermore, as each method returns the instance of PersonBuilder, it provides us a pseudo DomainSpecificLanguage.
但是,与构造器不同,此构造器不是不言自明的,并且可能会错误使用。 例如:
Person john = PersonBuilder.builder()
.build();
最简单的情况表明缺乏指导。 实际上,可以在创建阶段指导开发人员。 例如,假设我们要将创建过程分为四个步骤:
- 开发人员必须先设置名字,然后他可以设置姓氏然后他可以设置电子邮件最后他可以构建一个Person对象。
为此,我们必须创建四个接口:
public interface StepFirstnameBuilder {
StepLastnamePersonBuilder withFirstname(String firstname);
}
第一步使开发人员可以设置名字字段,然后使用第二步界面StepLastnamePersonBuilder。
public interface StepLastnamePersonBuilder {
StepEmailPersonBuilder withLastname(String lastname);
}
public interface StepEmailPersonBuilder {
FinalStepPersonBuilder withEmail(String email);
}
public interface FinalStepPersonBuilder {
Person build();
}
最后,设置完所有必填字段后,我们可以访问建立方法。 而已! 现在我们可以修改人建类来实现我们的四个步骤。
public class PersonBuilder implements StepFirstnameBuilder,
StepLastnamePersonBuilder,
StepEmailPersonBuilder,
FinalStepPersonBuilder {
private final Person person;
private PersonBuilder() {
person = new Person();
}
public static StepFirstnameBuilder builder() {
return new PersonBuilder();
}
@Override
public StepLastnamePersonBuilder withFirstname(String firstname) {
if (firstname == null) {
throw new IllegalArgumentException("firstname must be not null");
}
person.setFirstname(firstname);
return this;
}
@Override
public StepEmailPersonBuilder withLastname(String lastname) {
if (lastname == null) {
throw new IllegalArgumentException("lastname must be not null");
}
person.setLastname(lastname);
return this;
}
@Override
public FinalStepPersonBuilder withEmail(String email) {
person.setEmail(email);
return this;
}
@Override
public Person build() {
return person;
}
}
现在,让我们使用它:
Person john = PersonBuilder.builder() // -> returrns an instance of StepFirstnameBuilder
.withFirstname("john") // -> returns an instance of StepLastnamePersonBuilder
.withLastname("smith") // -> returns an instance of StepEmailPersonBuilder
.withEmail("john@smith") // -> returns an instance of FinalStepPersonBuilder
.build();
通过执行所有这些步骤,我们完全控制了Person对象的构建方式。 实际上,我们刚刚创建了DomainSpecificLanguage。 这种Builder模式是FluentInterface专门用于构建对象的一种特殊情况,它是表达对象构建方式的简便方法。
一种 big thanks to Sonyth and Mickael for their time and proofreading.
from: https://dev.to//vga/builder-pattern-a-first-step-to-dsl-47de
更多推荐
构建器模式,DSL的第一步
发布评论