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»:
Мы можем дополнительно настроить некоторые параметры класса построителя, такие как его имя и целевой пакет:
В результате мы сгенерировали класс 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
в правом верхнем углу окна параметров :
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
}
Кроме того, мы должны выбрать « Использовать существующие
» в окне параметров и указать на наш вновь созданный класс:
4. Использование плагина InnerBuilder
Давайте теперь посмотрим, как мы можем создать конструктор для класса Book с помощью плагина
InnerBuilder .
После того, как мы установили плагин, мы можем открыть всплывающее окно « Создать
», нажав Alt + Insert
(на ПК) и выбрав опцию Builder… :
Кроме того, мы можем напрямую вызвать плагин InnerBuilder, нажав Alt+Shift+B
(на ПК):
Как мы видим, есть несколько опций, которые мы можем выбрать для настройки сгенерированного компоновщика.
Давайте посмотрим, как создаётся билдер, когда все опции сняты:
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
.
Как мы видим, у нас есть три варианта настройки BookBuilder
:
Оставим все опции не отмеченными и посмотрим на сгенерированный класс билдера:
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.
Обычно лучше использовать такие инструменты для автоматического создания наших сборщиков . Каждый из представленных нами вариантов имеет свои плюсы и минусы. Какой подход мы на самом деле выбираем, это скорее вопрос вкуса и индивидуальных предпочтений.