Contents

IntelliJ中自动生成构建器类

1. 简介

Builder 设计模式 是使用最广泛的创建模式之一。它帮助我们构建复杂的对象。

手动编写构建器既麻烦又容易出错。因此,我们应该尽可能使用专用工具自动生成它们。

在本教程中,我们将探索在IntelliJ IDE 中自动创建构建器类的不同方法。我们将了解 IntelliJ 提供的开箱即用的内置功能以及第三方插件。

2. 初始设置

在本文中,我们将使用 IntelliJ IDEA 社区版的 2019.1.3 版本,这是撰写本文时的最新版本。但是,示例中介绍的所有技术也应该适用于任何其他版本的 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 的内置功能

要使用 IntelliJ 的内置工具为Book类生成构建器,我们需要一个合适的构造器。

让我们创建一个:

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

现在,我们准备创建一个构建器。因此,让我们将光标放在创建的构造函数上,然后按Ctrl+Alt+Shift+T(在 PC 上)打开Refactor This 弹出窗口,然后选择Replace Constructor with Builder refactoring:

/uploads/intellij_idea_java_builders/1.png

我们可以进一步调整构建器类的一些选项,比如它的名称和目标包:

/uploads/intellij_idea_java_builders/3.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.自定义设置器前缀

在构建器类中为 setter 方法使用with前缀是一种常见的做法。

要更改默认前缀,我们需要选择选项窗口右上角的Rename Setters Prefix图标:

/uploads/intellij_idea_java_builders/5.png

3.2. 静态内部生成器

我们中的一些人可能更喜欢将构建器实现为静态内部类,如 Joshua Bloch 在 Effective 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
}

此外,我们必须在选项窗口中选择Use existing并指向我们新创建的类:

/uploads/intellij_idea_java_builders/7.png

4. 使用 InnerBuilder 插件

现在让我们看看如何使用InnerBuilder 插件为Book类生成构建器。

安装插件后,我们可以通过按Alt+Insert(在 PC 上)并选择*Builder…*选项来打开Generate 弹出窗口:

/uploads/intellij_idea_java_builders/9.png

或者,我们可以通过按Alt+Shift+B(在 PC 上)直接调用 InnerBuilder 插件:

/uploads/intellij_idea_java_builders/11.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 是如何工作的。

同样,对于 InnerBuilder,我们可以按Alt+Insert(在 PC 上)并选择Builder选项或使用Alt+Shift+B快捷键。

/uploads/intellij_idea_java_builders/13.png

正如我们所看到的,我们可以从三个选项中选择自定义BookBuilder

/uploads/intellij_idea_java_builders/15.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 插件提供的第一个选项来自定义创建的构建器类 - 内部构建器 - 是不言自明的。

然而,另外两个更有趣,我们将在以下部分中探讨它们。

5.1. but方法选项

如果我们选择此选项,插件将向BookBuilder类添加一个*but()*方法:

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

这是创建在某些情况下可能派上用场的构建器类的有点不同的方法。