Всегда ли нужен Builder в Java?

в 0:00, , рубрики: Песочница

Про паттерн Builder сказано достаточно. Его достоинства очевидны. Есть изящные варианты замены (Элегантный Builder на Java) уменьшающие количество кода, и генераторы, вообще сводящие boilerplate на нет. Но иногда можно сделать еще проще, причем не прибегая к сторонним библиотекам.

Я использую подход с типизацией параметров:

// Удобный суперкласс для типизации объектных параметров
public class Value<V> {
    final V value;
    Value(V value) {
        this.value = value;
    }
}
//--------------------------------------------------------------------------------
public class PointFeatured{
    // Примитивные типы оборачиваются в классы напрямую
    public static final class Width {
        final int value;
        public Width(int value) {
            this.value = value;
        }
    }

    public static final class Height{
        final int value;
        public Height(int value) {
            this.value = value;
        }
    }

    // Объектные типы оборачиваются в наследников generic-класса Value<V>
    public static final class LabelA extends Value<String>{
        public LabelA(String value) {
            super(value);
        }
    }

    public static final class LabelB extends Value<String>{
        public LabelB(String value) {
            super(value);
        }
    }

    public final int width;
    public final int height;
    public final String labelA;
    public final String labelB;

    public PointFeatured(@NotNull Width width, @NotNull Height height,
                         @NotNull LabelA labelA, @NotNull LabelB labelB) {
        this.width = width.value;
        this.height = height.value;
        this.labelA= labelA.value;
        this.labelB = labelB.value;
    }
}

Как видно, классы типа Point(int x, int y) имеют всего пару полей, но от ошибок инициализации там тоже сложно удержаться. Искусственные внутренние типы Width и Height вносят однозначность в семантику инициализации.

Применение не сказать, чтобы было очень утомительное

    @Test
    public void withTypes() {

        PointFeatured point = new PointFeatured(
                new Width(10),
                new Height(20),
                new LabelA("A"),
                new LabelB("B"));
        Assert.assertEquals(10, point.width);
        Assert.assertEquals(20, point.height);
        Assert.assertEquals("A", point.labelA);
        Assert.assertEquals("B", point.labelB);

    }

Для примера, вот как бы это выглядело с применением Builder

public class PointWithBuilder{

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private int width;
        private int height;
        private String labelA;
        private String labelB;

        Builder() {
        }

        public Builder width(int width) {
            this.width = width;
            return this;
        }

        public Builder height(int height) {
            this.height = height;
            return this;
        }

        public Builder labelA(String labelA) {
            this.labelA = labelA;
            return this;
        }

        public Builder labelB(String labelB) {
            this.labelB = labelB;
            return this;
        }

        public PointWithBuilder build() {
            return new PointWithBuilder(width, height, labelA, labelB);
        }
    }

    public final int width;
    public final int height;
    public final String labelA;
    public final String labelB;

    private PointWithBuilder(int width, int height, String labelA, String labelB) {
        this.width = width;
        this.height = height;
        this.labelA= labelA;
        this.labelB = labelB;
    }

}

//---

    @Test
    public void withBuilder() {

        PointWithBuilder point = PointWithBuilder
                .builder()
                .width(10)
                .height(20)
                .labelA("A")
                .labelB("B")
                .build();
        Assert.assertEquals(10, point.width);
        Assert.assertEquals(20, point.height);
        Assert.assertEquals("A", point.labelA);
        Assert.assertEquals("B", point.labelB);

    }

Очевидно, что на малом количестве полей, да еще и объектного типа подход с типизацией параметров выигрывает у Builder по объему кода процентов на 30.

Автор: pull

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js