1. Introduction
1.绪论
The Builder design pattern is one of the most widely used creational patterns. It helps us to construct complex objects.
Builder设计模式是使用最广泛的创造模式之一。它可以帮助我们构建复杂的对象。
Writing builders by hand is cumbersome and error-prone. Therefore, we should use dedicated tools to auto-generate them whenever possible.
用手写构建器很麻烦而且容易出错。因此,我们应该尽可能使用专门的工具来自动生成它们。
In this tutorial, we’ll explore different ways to automatically create builder classes in the IntelliJ IDE. We’ll take a look at the built-in features IntelliJ provides out of the box, as well as at the third-party plugins.
在本教程中,我们将探讨在IntelliJ IDE 中自动创建构建器类的不同方法。我们将了解 IntelliJ 开箱即用的内置功能,以及第三方插件的情况。
2. Initial Setup
2.初始设置
Throughout this article, we’ll be using version 2019.1.3 of IntelliJ IDEA Community edition, which is the most recent release at the time of writing. However, all the techniques presented in the examples should work fine with any other version of IDEA as well.
在本文中,我们将使用 IntelliJ IDEA 社区版的 2019.1.3 版本,这是在撰写本文时的最新版本。然而,示例中介绍的所有技术在任何其他版本的IDEA中也应能正常工作。
Let’s begin with defining the Book class for which we’ll generate a builder:
让我们从定义Book类开始,我们将为其生成一个构建器。
public class Book {
private String title;
private Author author;
private LocalDate publishDate;
private int pageCount;
// standard constructor(s), getters and setters
}
3. Using IntelliJ’s Built-in Functionality
3.使用 IntelliJ 的内置功能
To generate a builder for the Book class using IntelliJ’s built-in tools, we need an appropriate constructor.
为了使用IntelliJ的内置工具为Book类生成一个构建器,我们需要一个合适的构建器。
Let’s create one:
让我们创造一个。
public Book(String title, Author author, LocalDate publishDate, int pageCount) {
this.title = title;
this.author = author;
this.publishDate = publishDate;
this.pageCount = pageCount;
}
Now, we’re ready to create a builder. Therefore, let’s place the cursor on the created constructor and open the Refactor This popup by pressing Ctrl+Alt+Shift+T (on PC) and select Replace Constructor with Builder refactoring:
现在,我们准备创建一个构建器。因此,让我们把光标放在已创建的构造器上,通过按Ctrl+Alt+Shift+T(在PC上)打开Refactor This弹出窗口,并选择Replace Constructor with Builder重构。
We can further adjust some of the options for the builder class, like its name and target package:
我们可以进一步调整构建器类的一些选项,比如它的名称和目标包。
As a result, we’ve generated the BookBuilder class:
因此,我们已经生成了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. Custom Setters Prefix
3.1.自定义设定器前缀
It’s a common practice to use a with prefix for setter methods in builder classes.
在构建器类中为setter方法使用with前缀是一种常见的做法。
To change the default prefix, we need to choose the Rename Setters Prefix icon in the upper right corner of the options window:
要改变默认前缀,我们需要选择选项窗口右上角的Rename Setters Prefix图标。
3.2. Static Inner Builder
3.2.静态内部构建器
Some of us may prefer to implement builders as static inner classes as described by Joshua Bloch in Effective Java.
我们中的一些人可能更喜欢将构建器作为静态的内部类来实现,正如Joshua Bloch在Effective Java中描述的那样。
If this is the case, we need to take a few extra steps to achieve this using IntelliJ’s Replace Constructor with Builder feature.
如果是这种情况,我们需要采取一些额外的步骤,使用IntelliJ的Replace Constructor with Builder功能来实现。
First of all, we need to manually create an empty inner class and make the constructor private:
首先,我们需要手动创建一个空的内层类,并使构造函数成为私有。
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
}
Furthermore, we have to choose Use existing in the options window and point to our newly created class:
此外,我们必须在选项窗口中选择使用现有的并指向我们新创建的类。
4. Using InnerBuilder Plugin
4.使用InnerBuilder插件
Let’s now see how we can generate a builder for the Book class using InnerBuilder plugin.
现在让我们看看如何使用InnerBuilder插件来为Book类生成一个构建器。。
Once we’ve installed the plugin, we can open the Generate pop-up by pressing Alt+Insert (on PC) and choosing the Builder… option:
一旦我们安装了这个插件,我们可以通过按Alt+Insert(在PC上)并选择Builder…选项来打开Generate弹出窗口。
Alternatively, we can call the InnerBuilder plugin directly by pressing Alt+Shift+B (on PC):
另外,我们可以通过按Alt+Shift+B(在PC上)直接调用InnerBuilder插件。
As we see, there are a few options we can choose from to customize the generated builder.
正如我们所看到的,有几个选项我们可以选择,以定制生成的构建器。
Let’s see the builder generated when all the options are unchecked:
让我们看看所有选项都不被选中时生成的构建器。
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);
}
}
The InnerBuilder plugin implements builders as static inner classes by default.
InnerBuilder插件默认将构建器实现为静态内部类。
5. Using Builder Generator Plugin
5.使用生成器插件
Finally, let’s see how Builder Generator works.
最后,让我们看看Builder Generator如何工作。
Similarly, as for InnerBuilder, we can either press Alt+Insert (on PC) and choose Builder option or use Alt+Shift+B shortcut.
同样,对于InnerBuilder,我们可以按Alt+Insert(在PC上)并选择Builder选项或使用Alt+Shift+B快捷方式。
As we can see, we have three options to choose from for customizing the BookBuilder:
我们可以看到,在定制BookBuilder时,我们有三个选项可以选择。
Let’s leave all the options unchecked and see the generated builder class:
让我们不勾选所有的选项,看看生成的构建器类。
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;
}
}
The first option that Builder Generator plugin provides to customize the created builder class – Inner builder – is rather self-explanatory.
生成器插件提供的第一个选项是自定义创建的生成器类–内部生成器–是相当不言自明的。
Two others, however, are more interesting, and we’ll explore them in the following sections.
然而,另外两个人更有趣,我们将在下面的章节中探讨他们。
5.1. ‘but’ Method Option
5.1 ‘but’方法选项
If we choose this option, the plugin will add a but() method to the BookBuilder class:
如果我们选择这个选项,该插件将给BookBuilder类添加一个but()方法。
public BookBuilder but() {
return aBook().withTitle(title).withAuthor(author)
.withPublishDate(publishDate).withPageCount(pageCount);
}
Now, let’s imagine we want to create three books with the same author and the same number of pages but with different titles and publish dates. We may create a base builder with common properties already set and then use the but() method to create new BookBuilders (and Books later on) out of it.
现在,让我们设想一下,我们想创建三本书,它们的作者相同,页数相同,但标题和出版日期不同。我们可以创建一个已经设置了通用属性的基础构建器,然后使用but()方法来创建新的BookBuilders(以及随后的Books)。
Let’s take a look at an example:
我们来看看一个例子。
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. Use a Single Field Option
5.2.使用单字段选项
If we choose this option, the generated builder will hold a reference to the created Book object instead of all the book’s properties:
如果我们选择这个选项,生成的构建器将持有对创建的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;
}
}
This is a bit different approach to create a builder class that might come in handy in certain situations.
这是一个有点不同的方法来创建一个构建器类,在某些情况下可能会派上用场。
6. Conclusion
6.结语
In this tutorial, we’ve explored different ways to generate builder classes in IntelliJ.
在本教程中,我们已经探讨了在IntelliJ中生成构建器类的不同方法。
It’s usually better to use these kinds of tools to automatically generate our builders. Each of the options we’ve presented has its pros and cons. Which approach we actually choose is rather a matter of taste and individual preferences.
通常情况下,使用这类工具来自动生成我们的构建器会更好。我们所介绍的每一种选择都有其优点和缺点。我们究竟选择哪种方法,而是一个品味和个人喜好的问题。