1. 简介

Builder 设计模式 是最常用的创建型设计模式之一。它能帮助我们构建结构复杂的对象。

手动编写 Builder 是繁琐且容易出错的,因此我们应尽可能使用工具来自动生成。

在本教程中,我们将探讨在 IntelliJ IDE 中自动生成 Builder 类的不同方式。我们会介绍 IntelliJ 自带的功能,以及一些常用的第三方插件。


2. 初始准备

本文使用的是 IntelliJ IDEA Community Edition 2019.1.3 版本,不过文中介绍的方法在其他版本中也应适用。

我们以一个 Book 类为例来演示 Builder 的生成:

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

    // 标准构造函数、getter 和 setter
}

3. 使用 IntelliJ 内置功能生成 Builder

要使用 IntelliJ 内置功能生成 Builder,首先需要一个合适的构造函数。

我们先为 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(Windows)调出 Refactor 弹窗,选择 Replace Constructor with Builder 功能:

Screenshot-2019-07-27-at-20.51.48

在弹出的窗口中可以设置 Builder 的类名和包路径:

Screenshot-2019-07-27-at-20.53.03

最终生成的 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 前缀

✅ Builder 类中常见的 setter 方法前缀是 with 而不是 set

要修改默认前缀,可以在配置窗口右上角点击 Rename Setters Prefix 按钮进行修改:

Screenshot-2019-07-27-at-20.54.03

3.2. 静态内部类实现 Builder

✅ 有些人更倾向于使用静态内部类来实现 Builder(如 Effective Java 中所推荐)。

要实现这一点,我们需要手动创建一个静态内部类,并将构造函数设为私有:

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

    // 标准 getter 和 setter
}

然后在 IntelliJ 的 Builder 配置窗口中选择 Use existing 并指向我们刚刚创建的 Builder 类:

Screenshot-2019-07-27-at-20.55.02


4. 使用 InnerBuilder 插件生成 Builder

InnerBuilder 插件是 IntelliJ 中一个非常流行的 Builder 生成插件。

安装插件后,按下 Alt+Insert(Windows)打开 Generate 菜单,选择 Builder…

Screenshot-2019-07-27-at-20.56.07

也可以直接按下 Alt+Shift+B 调出插件:

Screenshot-2019-07-27-at-20.56.14

不勾选任何选项时,生成的 Builder 类如下:

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

Builder Generator 是另一个非常实用的 Builder 生成插件。

同样,按下 Alt+InsertAlt+Shift+B 调出插件界面:

Screenshot-2019-07-27-at-20.57.51

插件提供三个选项用于定制 Builder:

Screenshot-2019-07-27-at-20.57.56

不勾选任何选项时,生成的 Builder 类如下:

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() 方法

如果启用该选项,插件会在 Builder 中添加 but() 方法,用于克隆当前 Builder 并重用部分配置:

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

示例使用:

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. 使用单一字段保存对象

启用该选项后,Builder 将不再保存各个字段,而是直接持有目标对象:

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

⚠️ 这种方式在某些场景下可能更方便,但也可能带来副作用,比如 Builder 修改对象后影响其他使用该对象的代码。


6. 总结

✅ 本教程我们介绍了在 IntelliJ 中生成 Builder 的三种方式:

  • 使用 IntelliJ 自带的 Replace Constructor with Builder 功能
  • 使用 InnerBuilder 插件
  • 使用 Builder Generator 插件

每种方式各有优劣,选择哪种方式主要取决于个人偏好和项目需求。

建议优先使用插件来自动生成 Builder,避免重复劳动和潜在错误。


原始标题:Creating the Java Builder for a Class in IntelliJ