Подтвердить что ты не робот

Шаблон строителя и большое количество обязательных параметров

На сегодняшний день я использую следующую реализацию шаблона построителя (в отличие от описанной реализации здесь):

public class Widget {
    public static class Builder {
        public Builder(String name, double price) { ... }
        public Widget build() { ... }
        public Builder manufacturer(String value) { ... }
        public Builder serialNumber(String value) { ... }
        public Builder model(String value) { ... }
    }

    private Widget(Builder builder) { ... }
}

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

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

Например, вместо:

Widget example = new Widget.Builder(req1, req2, req3,req4,req5,req6,req7,req8)
                           .addOptional(opt9)
                           .build();

сгруппируется следующим образом:

Object1 group1 = new Object1(req1, req2, req3, req4);
Object2 group2 = new Object2(req5, req6);

Widget example2 = new Widget.Builder(group1, group2, req7, req8)
                            .addOptional(opt9)
                            .build();

Несмотря на то, что отдельные объекты упрощают вещи совсем немного, это также затрудняет работу, если вы не знакомы с кодом. Одна вещь, которую я рассматривал, заключается в перемещении всех параметров в свои собственные методы addParam(param), а затем выполнение проверки по требуемым параметрам в методе build().

Какова наилучшая практика и, возможно, лучший подход к этому, который я не рассматривал?

4b9b3361

Ответ 1

Однако в последнее время я борется за то, чтобы понять, как шаблон имеет какую-либо пользу, когда все ваши параметры являются обязательными (или, по крайней мере, подавляющее большинство).

Свободный шаблон строителя по-прежнему полезен:

1) Его более читаемый - он эффективно разрешает именованные параметры, чтобы вызов был не просто длинным списком неназванных аргументов

2) Его неупорядоченный - это позволяет группировать аргументы вместе в логические группы, либо как часть одного вызова setbuilder, либо просто позволяя вам использовать естественный порядок вызова методов set set builder, которые имеют наибольший смысл этого конкретного конкретизации.

===

Widget example = new Widget.Builder(req1, req2, req3,req4,req5,req6,req7,req8)
                               .addOptional(opt9)
                               .build();

сгруппируется следующим образом:

Object1 group1 = new Object1(req1, req2, req3, req4);
Object2 group2 = new Object2(req5, req6);

    Widget example2 = new Widget.Builder(group1, group2, req7, req8)
                            .addOptional(opt9)
                            .build();

Несмотря на то, что отдельные объекты упрощают вещи совсем немного, это также затрудняет работу, если вы не знакомы с кодом. Одна вещь, которую я рассматривал, заключается в перемещении всех параметров в свои собственные методы addParam (param), а затем выполнение проверки на требуемые параметры в методе build().

Я предпочел бы гибрид, когда это было бы подходящим или естественным. Это не обязательно должно быть в конструкторе или каждый параметр имеет свой собственный метод addParam. Builder дает вам гибкость, чтобы сделать одно, другое, промежуточное или комбинированное:

Widget.Builder builder = new Widget.Builder(Widget.BUTTON);
builder.withWidgetBackingService(url, resource, id);
builder.withWidgetStyle(bgColor, lineWidth, fontStyle);
builder.withMouseover("Not required");
Widget example = builder.build();

Ответ 2

Вы можете использовать Step Builder, если у вас есть много обязательных параметров. Вкратце: вы определяете интерфейс для каждого обязательного параметра, а метод builder возвращает следующий обязательный интерфейс построителя или сам построитель для дополнительных методов. Строитель остается единственным классом, который реализует все интерфейсы.

Языки, такие как Kotlin и Scala, здесь более удобны, так как они предлагают именованные параметры со значениями по умолчанию.

Ответ 3

Одно из преимуществ шаблона Builder, которое я редко (если когда-либо) вижу в рекламе, заключается в том, что его можно также использовать для условной конструирования объекта, например, только если все обязательные параметры верны или доступны другие необходимые ресурсы. В этом отношении они предлагают аналогичные преимущества для статического метода factory.

Ответ 4

В последнее время я борюсь за то, чтобы понять, как шаблон если все ваши параметры обязательны

Шаблон упрощает создание неизменяемых классов и способствует считыванию кода. Рассмотрим класс Person ниже (с помощью обычного конструктора и строителя).

public static class Person {

    private static final class Builder {
        private int height, weight, age, income, rank;
        public Builder setHeight(final int height) { this.height = height; return this; }
        public Builder setWeight(final int weight) { this.weight = weight; return this; }
        public Builder setAge(final int age) { this.age = age; return this; }
        public Builder setIncome(final int income) {    this.income = income; return this; }
        public Builder setRank(final int rank) { this.rank = rank; return this; }
        public Person build() { return new Person(this); }
    }

    private final int height;
    private final int weight;
    private final int age;
    private final int income;
    private final int rank;

    public Person(final int height, final int weight, final int age, final int income, final int rank) {
        this.height = height; this.weight = weight; this.age = age; this.income = income; this.rank = rank;
    }

    private Person(final Builder builder) {
        height = builder.height; weight = builder.weight; age = builder.age; income = builder.income; rank = builder.rank;
        // Perform validation
    }

    public int getHeight() { return height; }
    public int getWeight() { return weight; }
    public int getAge() { return age; }
    public int getIncome() { return income; }
    public int getRank() {  return rank; }

}

Какой способ построения легче понять?

final Person p1 = new Person(163, 184, 48, 15000, 23);
final Person p2 = new Person.Builder().setHeight(163).setWeight(184).setAge(48).
    setIncome(15000).setRank(23).build();

Одним из способов обойти это было логическое объединение параметры передаются в свои классы

Конечно, это принцип cohesion и должен быть принят независимо от семантики построения объекта.

Ответ 5

Строитель/factory по-прежнему позволяет вам отделить интерфейс от типа реализации (или позволяет подключать адаптер и т.д.), предполагая, что Widget становится интерфейсом, и у вас есть способ ввести или скрыть new Widget.Builder.

Если вы не заботитесь о развязке, а ваша реализация - одноразовая, то вы правы: шаблон построителя не намного полезнее обычного конструктора (он по-прежнему помещает свои аргументы в атрибут- per-builder-method style.)

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

Widget.Builder base = new Widget.Builder(name, price).model("foo").manufacturer("baz");

// ...

Widget w1 = base.serialNumber("bar").build();
Widget w2 = base.serialNumber("baz").build();
Widget w3 = base.serialNumber("quux").build();

Это предполагает, что ваши строители неизменны: построители построителей не устанавливают атрибут и не возвращают this, а вместо этого возвращают новую копию себя с изменением. Как вы указали выше, объекты параметров - это еще один способ обойти шаблон повторяющихся аргументов. Там вам даже не нужен шаблон построителя: просто передайте объект параметра вашему конструктору реализации.