Java лучший способ реализовать шаблон построителя

Какое из следующего является лучшим подходом к реализации шаблона построителя?

1) Используя объект для сборки вместо всех его свойств в построителе (и создайте его в конструкторе конструктора):

public class Person {
    private String firstName;
    // other properties ...

    private Person() {}

    // getters ...

    public static class Builder {
        // person object instead of all the person properties
        private Person person;

        public Builder() {
            person = new Person();
        }

        public Builder setFirstName(String firstName) {
            person.firstName = firstName;

            return this;
        }

        // other setters ...

        public Person build() {
            if (null == person.firstName) {
                throw new IllegalStateException("Invalid data.");
            }

            return person;
        }
    }
}

2) Используя свойства объекта для сборки вместо объекта непосредственно в построителе (и создайте его в методе build()):

public class Person {
    private String firstName;
    // other properties ...

    private Person() {}

    // getters ...

    public static class Builder {
        // person properties instead of object
        private String firstName;
        // other properties ...

        public Builder() {}

        public Builder setFirstName(String firstName) {
            this.firstName = firstName;

            return this;
        }

        // other setters ...

        public Person build() {
            if (null == this.firstName) {
                throw new IllegalStateException("Invalid data.");
            }

            Person person = new Person();
            person.firstName = firstName;

            return person;
        }
    }
}

Я предпочитаю первый способ, потому что считаю, что с множеством свойств повторить их в построителе избыточно. Существуют ли некоторые недостатки с первым подходом?

Спасибо заранее и извините за мой плохой английский.

Ответ 1

Маленькое примечание. Да, свойства могут быть повторением, но у них есть преимущества.

Подробности ниже: Если вы посмотрите на детали здесь.

Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);

Проблема заключается в том, что, поскольку объект создается по нескольким вызовам, он может находиться в несогласованном состоянии частично через его конструкцию. Это также требует больших усилий для обеспечения безопасности потоков.

Лучшей альтернативой является использование шаблона Builder.

Обратите внимание, что метод в Builder и соответствующий конструктор или родительский класс Pizza - полный код в ссылке здесь

 public static class Builder {

    public Pizza build() {    // Notice this method
      return new Pizza(this);
    }
  }

  private Pizza(Builder builder) {  // Notice this Constructor
    size = builder.size;
    cheese = builder.cheese;
    pepperoni = builder.pepperoni;
    bacon = builder.bacon;
  }

Ответ 2

Шаблон Builder описан в книге "Банки четырех" "Образцы рисунков", в которой говорится:

Шаблон-конструктор - это шаблон проектирования, который позволяет поэтапно создавать сложные объекты с использованием правильной последовательности действий. Конструкция контролируется объектом-директором, который должен знать только тип объекта, который он должен создать.

Если существует последовательность шагов, которые должны выполняться при построении объекта, тогда перейдите к второй опции.

В вашем первом правиле правильная последовательность действий не контролируется. Вы можете перейти для любой опции, если последовательность действий не определена.

Ответ 3

Я думаю, что в вашем случае нет места, где можно создать объект. Строитель будет использоваться одинаково в обоих случаях с минимальной разницей в производительности.

Но если объект неизменен, и поле может быть создано более чем на один шаг, я бы, конечно, пошел со вторым подходом. Например, вы можете проверить исходный код java.lang.StringBuilder и увидеть, что объект String создан на последнем шаге:

public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

Кроме того, я предпочитаю шаблон мастера. Это расширение шаблона Builder, которое защищает вас от получения исключения IllegalStateException.

public class Person {

    private String firstName;
    // other properties ...

    private Person() {}

    // getters ...

    public static class Builder {

        public Builder() {}

        public FirstStep setFirstName(String firstName) {
            return new FirstStep(firstName);
        }

        public static class FirstStep {

            private String firstName;

            private FirstStep(String firstName) {
                this.firstName = firstName;
            }

            public Person build() {
                Person person = new Person();
                person.firstName = firstName;
                return person;
            }
        }
    }
}