Шаблон Builder, вероятно, является одним из самых простых (кроме синглтонов) для реализации и использования. Это очень удобно использовать, когда у сущности много конструкторов, и внешнему пользователю сущности становится очень трудно решить, какой из них использовать.
Шаблон Builder помогает избежать такой путаницы, потому что пользователь может решить, для каких полей устанавливать значения, а для каких нет.
Кроме того, мне нравится использовать шаблон Builder, когда у меня есть объект, который состоит из множества полей, и мне нужно протестировать какой-либо сервис, но перед этим мне нужно установить некоторую информацию для объекта. Допустим, у нас есть сущность Person:
public class Person {
private String firstName;
private String lastName;
private LocalDate birthDate;
private String addressOne;
private String addressTwo;
private String sex;
private boolean driverLicence;
private boolean married;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public LocalDate getBirthDate() {
return birthDate;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
public String getAddressOne() {
return addressOne;
}
public void setAddressOne(String addressOne) {
this.addressOne = addressOne;
}
public String getAddressTwo() {
return addressTwo;
}
public void setAddressTwo(String addressTwo) {
this.addressTwo = addressTwo;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public boolean isDriverLicence() {
return driverLicence;
}
public void setDriverLicence(boolean driverLicence) {
this.driverLicence = driverLicence;
}
public boolean isMarried() {
return married;
}
public void setMarried(boolean married) {
this.married = married;
}
И у меня есть сервис, который необходимо протестировать, но перед этим мне нужно установить некоторую информацию для объекта. Я мог бы создать сущность с определенной информацией в моем тесте следующим образом:
private Person createPersonForTesting() {
Person person = new Person();
person.setFirstName("FirstName");
person.setLastName("LastName");
person.setAddressOne("Address1");
...
return person;
}
Но на данный момент он выглядит не очень хорошо, он занимает немного больше места и требует больше времени для написания этого метода (и скорость для нас, разработчиков, очень важна, не так ли?). Также существует риск пропустить какое-то поле.
Чтобы избежать всех упомянутых проблем, лучше использовать шаблон проектирования Builder:
private Person createPersonForTesting() {
return Person.builder()
.firstName("FirstName")
.lastName("LastName")
.addressOne("AddressOne")
.addressTwo("AddressTwo")
.birthDate(LocalDate.of(1995, Month.APRIL, 13))
.sex("male")
.driverLicence(true)
.married(true)
.build();
}
Это выглядит намного лучше, не так ли?
И вот мой строитель:
public class Person {
private String firstName;
private String lastName;
private LocalDate birthDate;
private String addressOne;
private String addressTwo;
private String sex;
private boolean driverLicence;
private boolean married;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public LocalDate getBirthDate() {
return birthDate;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
public String getAddressOne() {
return addressOne;
}
public void setAddressOne(String addressOne) {
this.addressOne = addressOne;
}
public String getAddressTwo() {
return addressTwo;
}
public void setAddressTwo(String addressTwo) {
this.addressTwo = addressTwo;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public boolean isDriverLicence() {
return driverLicence;
}
public void setDriverLicence(boolean driverLicence) {
this.driverLicence = driverLicence;
}
public boolean isMarried() {
return married;
}
public void setMarried(boolean married) {
this.married = married;
}
public static PersonBuilder builder() {
return new PersonBuilder();
}
public static class PersonBuilder {
private String firstName;
private String lastName;
private LocalDate birthDate;
private String addressOne;
private String addressTwo;
private String sex;
private boolean driverLicence;
private boolean married;
public PersonBuilder firstName(String firstName) {
this.firstName = firstName;
return this;
}
public PersonBuilder lastName(String lastName) {
this.lastName = lastName;
return this;
}
public PersonBuilder birthDate(LocalDate birthDate) {
this.birthDate = birthDate;
return this;
}
public PersonBuilder addressOne(String addressOne) {
this.addressOne = addressOne;
return this;
}
public PersonBuilder addressTwo(String addressTwo) {
this.addressTwo = addressTwo;
return this;
}
public PersonBuilder sex(String sex) {
this.sex = sex;
return this;
}
public PersonBuilder driverLicence(boolean driverLicence) {
this.driverLicence = driverLicence;
return this;
}
public PersonBuilder married(boolean married) {
this.married = married;
return this;
}
public Person build() {
Person person = new Person();
person.firstName = this.firstName;
person.lastName = this.lastName;
person.addressOne = this.addressOne;
person.addressTwo = this.addressTwo;
person.birthDate = this.birthDate;
person.sex = this.sex;
person.driverLicence = this.driverLicence;
person.married = this.married;
return person;
}
}
}
Обратите внимание, что я использую шаблон сборки немного по-другому, чем «официально это согласовано» (посмотрите на пример вики-Java http://en.wikipedia.org/wiki/Builder_pattern
Вместо частного конструктора класса Person, который бы принимал объект PersonBuilder, я создаю статический метод builder (), который возвращает объект PersonBuilder. И затем, используя этот объект-строитель, я создаю Person.
Я делаю так из-за двух вещей:
Прежде всего, если я выполняю рефакторинг старого кода и допустим, что объект Person создается во многих местах с конструктором по умолчанию без аргументов, создание частного конструктора в классе Person с аргументами вынуждает меня создать еще один открытый конструктор без аргументов ( это ненужный код) или иди и переписать код с помощью компоновщика.
Во-вторых, наличие длинного конструктора в классе Person делает его довольно уродливым, на мой взгляд. Конечно, можно переместить конструктор в конец класса, но я предпочитаю иметь его во вложенном статическом классе, так как он дает какое-то разделение.
Пример исходного кода доступен здесь .