Статьи

Использование Pattern Builder в реальной жизни

Шаблон 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 делает его довольно уродливым, на мой взгляд. Конечно, можно переместить конструктор в конец класса, но я предпочитаю иметь его во вложенном статическом классе, так как он дает какое-то разделение.

Пример исходного кода доступен  здесь .