«1. Введение

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

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

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

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

В этой статье мы будем использовать версию 2019.1.3 редакции IntelliJ IDEA Community, которая является самой последней версией на момент написания. Однако все методы, представленные в примерах, должны работать и с любой другой версией 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»:

Мы можем дополнительно настроить некоторые параметры для 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. Статический внутренний построитель

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

Если это так, нам нужно сделать несколько дополнительных шагов, чтобы добиться этого, используя функцию IntelliJ Replace Constructor with 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 (на ПК):

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

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

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

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);
    }
}

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

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

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

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

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

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

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;
    }
}

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

5.1. Опция метода «but»

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

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

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

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

5.2. Используйте опцию одного поля

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();

«Если мы выберем эту опцию, сгенерированный построитель будет содержать ссылку на созданный объект 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.

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

«