- PVSM.RU - https://www.pvsm.ru -

Билдеры или конструкторы? Рассуждаем вслух

Всем привет! Хочу порассуждать над целесообразностью использования билдеров для не сложных объектов.

Для упрощения буду использовать аннотации lombok'a:

@Value [1]
@Builder [2]

Недолго погуглив, получаем, что builder Отделяет конструирование сложного объекта от его представления так, что в результате одного и того же процесса конструирования могут получаться разные представления. [3] Только ли для сложных объектов?

Рассмотрим на простом примере:

@Value
public class Info {
    @Nullable String uuid;
    @Nullable String email;
    @Nullable String phone;
}

Довольно таки простой класс. На деле получаем иммутабельный объект, который инициализируется через конструктор.

Но, как мы видим, все поля nullable, и создание такие объектов будет выглядеть не очень красиво:

        final Info info1 = new Info(null, "email@email.com", "79998888888");
        final Info info2 = new Info("3d107928-d225-11ea-87d0-0242ac130003", null, null);
        final Info info3 = new Info("3d107928-d225-11ea-87d0-0242ac130003 ", "email@email.com", null);
...

Безусловно, есть варианты:

  1. Объекты, где немного полей разных типов, можно завезти несколько конструкторов. Но это не решает проблему класса выше.
  2. Использовать setter'ы — субьективно, нагромождает код.


А что с билдером?

@Value
@Builder
public class Info {
    @Nullable String uuid;
    @Nullable String email;
    @Nullable String phone;
}

Мы получаем весьма элегантное построение не сложного объекта:

        final Info info1 = Info.builder()
                .uuid("3d107928-d225-11ea-87d0-0242ac130003")
                .phone("79998888888")
                .build();
        final Info2 info2 = Info.builder()
                .email("email@email.com")
                .phone("79998888888")
                .build();
...
}

Однако, для использование в проекте jackson'а [4], необходимо дополнить наш клас, чтобы он успешно десериализовывался:

@Value
@Builder(builderClassName = "InfoBuilder")
@JsonDeserialize(builder = Info.InfoBuilder.class)
public class Info {
    @Nullable String uuid;
    @Nullable String email;
    @Nullable String phone;

    @JsonPOJOBuilder(withPrefix = "")
    public static class InfoBuilder {

    }
}

Получаем свои плюсы и минусы за оба подхода:

builder:

+
1. Код становится лаконичнее.
3. null в параметрах контруктора не бросается в глаза.
2. Меньше шанс перепутать параметры одного типа.
-
1. Создаем лишний объект, который GC в целом благополучно уберет, но забывать об этом не стоит.
2. При необходимости использовать jacson — нагромоздим класс.

конструктор:

+
1. Минимально нагромождает наш класс, никакой воды.
2. Нет создания лишних объектов.
-
1. Весьма часто в конструктор такого объекта будет прилетать null.
2. Есть вероятность ошибится, когда кто-то будет вносить изменения в код.

Итог

Опираясь на свой опыт — склоняюсь к использованию билдеров. Плата за это не высока, а на выходе имеем код, который приятно читать.

И конечно же, пишите тесты, чтобы избежать 2-го отрицательного пункта использования контрукторов.

P.S. Это моя первая статья, буду благодарен конструктивной критике и комментариям.

Автор: Иван

Источник [5]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/java/356025

Ссылки в тексте:

[1] @Value: https://projectlombok.org/features/Value

[2] @Builder: https://projectlombok.org/features/Builder

[3] Отделяет конструирование сложного объекта от его представления так, что в результате одного и того же процесса конструирования могут получаться разные представления.: https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D1%80%D0%BE%D0%B8%D1%82%D0%B5%D0%BB%D1%8C_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)#%D0%A6%D0%B5%D0%BB%D1%8C

[4] jackson'а: https://github.com/FasterXML/jackson

[5] Источник: https://habr.com/ru/post/515358/?utm_source=habrahabr&utm_medium=rss&utm_campaign=515358