Рубрики
Без рубрики

Автоматическая генерация шаблона Builder с помощью Free Builder

Узнайте, как создавать объекты Java builder с помощью бесплатной библиотеки Builder

Автор оригинала: baeldung.

1. Обзор

В этом уроке мы будем использовать библиотеку Free Builder library для создания классов builder в Java.

2. Шаблон Проектирования Строителя

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

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

3. Реализация Builder в Java

Прежде чем мы перейдем к Free Builder, давайте реализуем шаблонный конструктор для нашего класса Employee :

public class Employee {

    private final String name;
    private final int age;
    private final String department;

    private Employee(String name, int age, String department) {
        this.name = name;
        this.age = age;
        this.department = department;
    }
}

И внутренний Строитель класс:

public static class Builder {

    private String name;
    private int age;
    private String department;

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

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

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

    public Employee build() {
        return new Employee(name, age, department);
    }
}

Соответственно, теперь мы можем использовать конструктор для создания экземпляра объекта Employee :

Employee.Builder emplBuilder = new Employee.Builder();

Employee employee = emplBuilder
  .setName("baeldung")
  .setAge(12)
  .setDepartment("Builder Pattern")
  .build();

Как показано выше, для реализации класса builder необходимо много шаблонного кода.

В последующих разделах мы увидим, как Free Builder может мгновенно упростить эту реализацию.

4. Зависимость Maven

Чтобы добавить библиотеку Free Builder, мы добавим зависимость Free Builder Maven в ваш pom.xml :


    org.inferred
    freebuilder
    2.4.1

5. Бесплатная аннотация Конструктора

5.1. Создание конструктора

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

Мы аннотируем ваш Employee класс из предыдущего раздела с помощью @ Free Builder | и посмотрим, как он автоматически генерирует класс builder:

@FreeBuilder
public interface Employee {
 
    String name();
    int age();
    String department();
    
    class Builder extends Employee_Builder {
    }
}

Важно отметить, что Employee теперь является интерфейсом |, а не классом POJO. Кроме того, он содержит все атрибуты объекта Employee в виде методов.

Прежде чем мы продолжим использовать этот конструктор, мы должны настроить наши идеи, чтобы избежать каких-либо проблем с компиляцией. Поскольку Free Builder автоматически генерирует класс Employee_Builder во время компиляции, IDE обычно жалуется на ClassNotFoundException в строке № 8 .

Чтобы избежать таких проблем, нам нужно включить обработку аннотаций в IntelliJ или Eclipse . И при этом мы будем использовать процессор аннотаций Free Builder org.inferred.freebuilder.processor.Процессор. Кроме того, каталог, используемый для создания этих исходных файлов, должен быть помечен как Generated Sources Root .

Кроме того, мы также можем выполнить mvn install для сборки проекта и создания необходимых классов builder.

Наконец, мы скомпилировали наш проект и теперь можем использовать Employee.Строитель класс:

Employee.Builder builder = new Employee.Builder();
 
Employee employee = builder.name("baeldung")
  .age(10)
  .department("Builder Pattern")
  .build();

В общем, есть два основных отличия между этим классом и классом builder, который мы видели ранее. Во-первых, мы должны установить значение для всех атрибутов класса Employee . В противном случае он выдает исключение IllegalStateException .

Мы увидим, как Free Builder обрабатывает необязательные атрибуты в следующем разделе.

Во-вторых, имена методов Employee.Builder не следует соглашениям об именовании JavaBean. Мы увидим это в следующем разделе.

5.2. Соглашение об именовании JavaBean

Чтобы заставить Free Builder следовать соглашению об именовании JavaBean, мы должны переименовать ваши методы в Employee и префиксировать методы с помощью get :

@FreeBuilder
public interface Employee {
 
    String getName();
    int getAge();
    String getDepartment();

    class Builder extends Employee_Builder {
    }
}

Это приведет к созданию геттеров и сеттеров, которые следуют соглашению об именовании JavaBean:

Employee employee = builder
  .setName("baeldung")
  .setAge(10)
  .setDepartment("Builder Pattern")
  .build();

5.3. Методы Картографирования

В сочетании с геттерами и сеттерами Free Builder также добавляет методы mapper в класс builder. Эти методы отображения принимают Унарный оператор в качестве входных данных, тем самым позволяя разработчикам вычислять сложные значения полей.

Предположим, что наш класс Employee также имеет поле зарплаты:

@FreeBuilder
public interface Employee {
    Optional getSalaryInUSD();
}

Теперь предположим, что нам нужно конвертировать валюту зарплаты, которая предоставляется в качестве входных данных:

long salaryInEuros = INPUT_SALARY_EUROS;
Employee.Builder builder = new Employee.Builder();

Employee employee = builder
  .setName("baeldung")
  .setAge(10)
  .mapSalaryInUSD(sal -> salaryInEuros * EUROS_TO_USD_RATIO)
  .build();

Free Builder предоставляет такие методы отображения для всех полей.

6. Значения по умолчанию и проверки ограничений

6.1. Установка Значений По Умолчанию

Сотрудник .Реализация Builder , которую мы обсуждали до сих пор, ожидает, что клиент передаст значения для всех полей. На самом деле он проваливает процесс инициализации с помощью IllegalStateException в случае отсутствия полей.

Чтобы избежать таких сбоев, мы можем либо установить значения по умолчанию для полей, либо сделать их необязательными .

Мы можем установить значения по умолчанию в Employee.Builder конструктор:

@FreeBuilder
public interface Employee {

    // getter methods

    class Builder extends Employee_Builder {

        public Builder() {
            setDepartment("Builder Pattern");
        }
    }
}

Поэтому мы просто устанавливаем значение по умолчанию department в конструкторе. Это значение будет применяться ко всем объектам Employee .

6.2. Проверка Ограничений

Обычно у нас есть определенные ограничения на значения полей. Например, действительное электронное письмо должно содержать букву “@” или возраст сотрудника должен находиться в пределах определенного диапазона.

Такие ограничения требуют от нас проверки входных значений. И Free Builder позволяет нам добавлять эти проверки, просто переопределяя методы setter :

@FreeBuilder
public interface Employee {

    // getter methods

    class Builder extends Employee_Builder {

        @Override
        public Builder setEmail(String email) {
            if (checkValidEmail(email))
                return super.setEmail(email);
            else
                throw new IllegalArgumentException("Invalid email");

        }

        private boolean checkValidEmail(String email) {
            return email.contains("@");
        }
    }
}

7. Необязательные значения

7.1. Использование Необязательных Полей

Некоторые объекты содержат необязательные поля, значения для которых могут быть пустыми или нулевыми. Free Builder позволяет нам определять такие поля с помощью Java Optional type :

@FreeBuilder
public interface Employee {

    String getName();
    int getAge();

    // other getters
    
    Optional getPermanent();

    Optional getDateOfJoining();

    class Builder extends Employee_Builder {
    }
}

Теперь мы можем пропустить предоставление любого значения для Необязательных полей:

Employee employee = builder.setName("baeldung")
  .setAge(10)
  .setPermanent(true)
  .build();

Примечательно, что мы просто передали значение для поля permanent вместо Optional. Поскольку мы не задали значение для поля дата присоединения , оно будет Optional.empty() которое используется по умолчанию для полей Optional .

7.2. Использование полей @Nullable

Хотя использование Optional рекомендуется для обработки null s в Java, FreeBuilder позволяет us использовать @Nullable | для обратной совместимости :

@FreeBuilder
public interface Employee {

    String getName();
    int getAge();
    
    // other getter methods

    Optional getPermanent();
    Optional getDateOfJoining();

    @Nullable String getCurrentProject();

    class Builder extends Employee_Builder {
    }
}

Использование Optional в некоторых случаях не рекомендуется, что является еще одной причиной, по которой @Nullable предпочтительнее для классов builder.

8. Коллекции и карты

Free Builder имеет специальную поддержку коллекций и карт:

@FreeBuilder
public interface Employee {

    String getName();
    int getAge();
    
    // other getter methods

    List getAccessTokens();
    Map getAssetsSerialIdMapping();


    class Builder extends Employee_Builder {
    }
}

Free Builder добавляет удобные методы для добавления входных элементов в коллекцию в классе builder :

Employee employee = builder.setName("baeldung")
  .setAge(10)
  .addAccessTokens(1221819L)
  .addAccessTokens(1223441L, 134567L)
  .build();

Существует также метод getaccesstoken() в классе builder, который возвращает неизменяемый список . Аналогично, для Map:

Employee employee = builder.setName("baeldung")
  .setAge(10)
  .addAccessTokens(1221819L)
  .addAccessTokens(1223441L, 134567L)
  .putAssetsSerialIdMapping("Laptop", 12345L)
  .build();

Метод getter для Map также возвращает неизменяемую карту в клиентский код.

9. Вложенные строители

Для реальных приложений нам, возможно, придется вложить много объектов ценности для наших доменных сущностей . А поскольку вложенные объекты сами могут нуждаться в реализациях builder, Free Builder допускает вложенные сборные типы.

Например, предположим, что у нас есть вложенный сложный тип Address в классе Employee :

@FreeBuilder
public interface Address {
 
    String getCity();

    class Builder extends Address_Builder {
    }
}

Теперь Free Builder генерирует setter методы, которые принимают Адрес.Builder в качестве входных данных вместе с Адресом типом:

Address.Builder addressBuilder = new Address.Builder();
addressBuilder.setCity(CITY_NAME);

Employee employee = builder.setName("baeldung")
  .setAddress(addressBuilder)
  .build();

Примечательно, что Free Builder также добавляет метод для настройки существующего Адреса объекта в Employee :

Employee employee = builder.setName("baeldung")
  .setAddress(addressBuilder)
  .mutateAddress(a -> a.setPinCode(112200))
  .build();

Наряду с типами Free Builder Free Builder также допускает вложенность других строителей, таких как protos .

10. Строительство Частичного Объекта

Как мы уже обсуждали ранее, Free Builder выдает исключение IllegalStateException для любого нарушения ограничений — например, пропущенных значений для обязательных полей.

Хотя это желательно для производственных сред , это усложняет модульное тестирование, которое не зависит от ограничений в целом .

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

Employee employee = builder.setName("baeldung")
  .setAge(10)
  .setEmail("[email protected]")
  .buildPartial();

assertNotNull(employee.getEmail());

Таким образом, даже если мы не установили все обязательные поля для Employee , мы все равно можем проверить, что поле email имеет допустимое значение.

11. Пользовательский метод toString()

С объектами value нам часто приходится добавлять пользовательскую реализацию toString () . Free Builder позволяет это сделать через абстрактные классы:

@FreeBuilder
public abstract class Employee {

    abstract String getName();

    abstract int getAge();

    @Override
    public String toString() {
        return getName() + " (" + getAge() + " years old)";
    }

    public static class Builder extends Employee_Builder{
    }
}

Мы объявили Employee абстрактным классом, а не интерфейсом, и предоставили пользовательскую реализацию toString () .

12. Сравнение с другими библиотеками Builder

Реализация builder, которую мы обсуждали в этой статье , очень похожа на реализацию Lombok , Immutables или любого другого процессора аннотаций . Однако есть несколько отличительных характеристик которые мы уже обсуждали:

    • Методы картографирования
    • Вложенные Сборные Типы
    • Частичные объекты

13. Заключение

В этой статье мы использовали бесплатную библиотеку Builder для создания класса builder на Java. Мы реализовали различные настройки класса builder с помощью аннотаций, тем самым сократив шаблонный код, необходимый для его реализации .

Мы также увидели, чем Free Builder отличается от некоторых других библиотек, и кратко обсудили некоторые из этих характеристик в этой статье.

Все примеры кода доступны на GitHub .