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 功能:
在弹出的窗口中可以设置 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. 自定义 Setter 前缀
✅ Builder 类中常见的 setter 方法前缀是 with
而不是 set
。
要修改默认前缀,可以在配置窗口右上角点击 Rename Setters Prefix 按钮进行修改:
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
类:
4. 使用 InnerBuilder 插件生成 Builder
✅ InnerBuilder 插件是 IntelliJ 中一个非常流行的 Builder 生成插件。
安装插件后,按下 Alt+Insert
(Windows)打开 Generate 菜单,选择 Builder…:
也可以直接按下 Alt+Shift+B
调出插件:
不勾选任何选项时,生成的 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+Insert
或 Alt+Shift+B
调出插件界面:
插件提供三个选项用于定制 Builder:
不勾选任何选项时,生成的 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,避免重复劳动和潜在错误。