Перейти к основному содержимому

Создание Java Builder для класса в IntelliJ

· 6 мин. чтения

1. Введение

Шаблон проектирования Builder является одним из наиболее широко используемых шаблонов создания. Это помогает нам создавать сложные объекты.

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

В этом руководстве мы рассмотрим различные способы автоматического создания классов построителей в IntelliJ IDE. Мы рассмотрим встроенные функции, которые IntelliJ предоставляет «из коробки», а также сторонние плагины.

2. Первоначальная настройка

В этой статье мы будем использовать версию 2019.1.3 IntelliJ IDEA Community edition, которая является самой последней версией на момент написания. Однако все методы, представленные в примерах, должны работать и с любой другой версией IDEA.

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

public class Book {
private String title;
private Author author;
private LocalDate publishDate;
private int pageCount;

// standard constructor(s), getters and setters
}

3. Использование встроенной функциональности IntelliJ

Чтобы сгенерировать построитель для класса Book с помощью встроенных инструментов IntelliJ, нам нужен соответствующий конструктор.

Давайте создадим один:

public Book(String title, Author author, LocalDate publishDate, int pageCount) {
this.title = title;
this.author = author;
this.publishDate = publishDate;
this.pageCount = pageCount;
}

Теперь мы готовы создать конструктор. Поэтому давайте поместим курсор на созданный конструктор и откроем всплывающее окно Refactor This , нажав Ctrl+Alt+Shift+T (на ПК) и выбрав « Заменить конструктор на рефакторинг Builder»:

./5eab4f730aa96cb4877e06508f3e7087.png

Мы можем дополнительно настроить некоторые параметры класса построителя, такие как его имя и целевой пакет:

./7556b1ab02d8752e8d8855fa5a952ffb.png

В результате мы сгенерировали класс BookBuilder :

public class BookBuilder {
private String title;
private Author author;
private LocalDate publishDate;
private int pageCount;

public BookBuilder setTitle(String title) {
this.title = title;
return this;
}

public BookBuilder setAuthor(Author author) {
this.author = author;
return this;
}

public BookBuilder setPublishDate(LocalDate publishDate) {
this.publishDate = publishDate;
return this;
}

public BookBuilder setPageCount(int pageCount) {
this.pageCount = pageCount;
return this;
}

public Book createBook() {
return new Book(title, author, publishDate, pageCount);
}
}

3.1. Префикс пользовательских сеттеров

Обычной практикой является использование префикса with для методов установки в классах построителей.

Чтобы изменить префикс по умолчанию, нам нужно выбрать значок Rename Setters Prefix в правом верхнем углу окна параметров :

./a3645e8b72f95bc78aa8e730ac51c907.png

3.2. Статический внутренний строитель

Некоторые из нас могут предпочесть реализовать конструкторы как статические внутренние классы, как описано Джошуа Блохом в книге Effective Java .

Если это так, нам нужно сделать несколько дополнительных шагов, чтобы добиться этого, используя функцию IntelliJ « Заменить конструктор на Builder ».

Прежде всего, нам нужно вручную создать пустой внутренний класс и сделать конструктор приватным:

public class Book {

private String title;
private Author author;
private LocalDate publishDate;
private int pageCount;

public static class Builder {

}

private Book(String title, Author author, LocalDate publishDate, int pageCount) {
this.title = title;
this.author = author;
this.publishDate = publishDate;
this.pageCount = pageCount;
}

// standard getters and setters
}

Кроме того, мы должны выбрать « Использовать существующие » в окне параметров и указать на наш вновь созданный класс:

./3c909f7eeec37853bac175dc31188101.png

4. Использование плагина InnerBuilder

Давайте теперь посмотрим, как мы можем создать конструктор для класса Book с помощью плагина InnerBuilder .

После того, как мы установили плагин, мы можем открыть всплывающее окно « Создать », нажав Alt + Insert (на ПК) и выбрав опцию Builder… :

./fd6c97752faabd0341d3d933b6098c85.png

Кроме того, мы можем напрямую вызвать плагин InnerBuilder, нажав Alt+Shift+B (на ПК):

./5e48a0c640ef7d4c30476e3033904ef0.png

Как мы видим, есть несколько опций, которые мы можем выбрать для настройки сгенерированного компоновщика.

Давайте посмотрим, как создаётся билдер, когда все опции сняты:

public static final class Builder {
private String title;
private Author author;
private LocalDate publishDate;
private int pageCount;

public Builder() {
}

public Builder title(String val) {
title = val;
return this;
}

public Builder author(Author val) {
author = val;
return this;
}

public Builder publishDate(LocalDate val) {
publishDate = val;
return this;
}

public Builder pageCount(int val) {
pageCount = val;
return this;
}

public Book build() {
return new Book(this);
}
}

Плагин InnerBuilder по умолчанию реализует конструкторы как статические внутренние классы.

5. Использование плагина Builder Generator

Наконец, давайте посмотрим, как работает Builder Generator .

Точно так же, как и для InnerBuilder, мы можем либо нажать Alt+Insert (на ПК) и выбрать опцию Builder , либо использовать сочетание клавиш Alt+Shift+B .

./b76260e17be066d798d24cb2d2452691.png

Как мы видим, у нас есть три варианта настройки BookBuilder :

./0cc72f29bd8fc319b801e41c4932baaf.png

Оставим все опции не отмеченными и посмотрим на сгенерированный класс билдера:

public final class BookBuilder {
private String title;
private Author author;
private LocalDate publishDate;
private int pageCount;

private BookBuilder() {
}

public static BookBuilder aBook() {
return new BookBuilder();
}

public BookBuilder withTitle(String title) {
this.title = title;
return this;
}

public BookBuilder withAuthor(Author author) {
this.author = author;
return this;
}

public BookBuilder withPublishDate(LocalDate publishDate) {
this.publishDate = publishDate;
return this;
}

public BookBuilder withPageCount(int pageCount) {
this.pageCount = pageCount;
return this;
}

public Book build() {
Book book = new Book();
book.setTitle(title);
book.setAuthor(author);
book.setPublishDate(publishDate);
book.setPageCount(pageCount);
return book;
}
}

Первый вариант, который плагин Builder Generator предоставляет для настройки созданного класса строителя — Inner builder — довольно понятен.

Два других, однако, более интересны, и мы рассмотрим их в следующих разделах.

5.1. Вариант метода «но»

Если мы выберем эту опцию, плагин добавит метод but() в класс BookBuilder :

public BookBuilder but() {
return aBook().withTitle(title).withAuthor(author)
.withPublishDate(publishDate).withPageCount(pageCount);
}

Теперь давайте представим, что мы хотим создать три книги с одним и тем же автором и одинаковым количеством страниц, но с разными названиями и датами публикации. Мы можем создать базовый построитель с уже установленными общими свойствами, а затем использовать метод but() для создания из него новых BookBuilder (а позже и Book ).

Давайте рассмотрим пример:

BookBuilder commonBuilder = BookBuilder.aBook().withAuthor(johnDoe).withPageCount(123);

Book my_first_book = commonBuilder.but()
.withPublishDate(LocalDate.of(2017, 12, 1))
.withTitle("My First Book").build();

Book my_second_book = commonBuilder.but()
.withPublishDate(LocalDate.of(2018, 12, 1))
.withTitle("My Second Book").build();

Book my_last_book = commonBuilder.but()
.withPublishDate(LocalDate.of(2019, 12, 1))
.withTitle("My Last Book").build();

5.2. Используйте вариант с одним полем

Если мы выберем эту опцию, сгенерированный построитель будет содержать ссылку на созданный объект Book вместо всех свойств книги:

public final class BookBuilder {
private Book book;

private BookBuilder() {
book = new Book();
}

public static BookBuilder aBook() {
return new BookBuilder();
}

public BookBuilder withTitle(String title) {
book.setTitle(title);
return this;
}

public BookBuilder withAuthor(Author author) {
book.setAuthor(author);
return this;
}

public BookBuilder withPublishDate(LocalDate publishDate) {
book.setPublishDate(publishDate);
return this;
}

public BookBuilder withPageCount(int pageCount) {
book.setPageCount(pageCount);
return this;
}

public Book build() {
return book;
}
}

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

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

В этом руководстве мы рассмотрели различные способы создания классов построителей в IntelliJ.

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