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 { OptionalgetSalaryInUSD(); }
Теперь предположим, что нам нужно конвертировать валюту зарплаты, которая предоставляется в качестве входных данных:
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 OptionalgetPermanent(); 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 OptionalgetPermanent(); 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 ListgetAccessTokens(); 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 .