Pull to refresh

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

Java *
Awaiting invitation
Про паттерн 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.
Tags:
Hubs:
You can’t comment this post because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.