💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# Java 中的构建器设计模式 > 原文: [https://howtodoinjava.com/design-patterns/creational/builder-pattern-in-java/](https://howtodoinjava.com/design-patterns/creational/builder-pattern-in-java/) 顾名思义, [**构建器模式**](https://en.wikipedia.org/wiki/Builder_pattern "Builder Pattern")是构建复杂对象的**替代方法**。 仅当您要使用相同的对象构建过程构建不同的不可变对象时,才应使用此方法。 在开始讨论之前,我想弄清楚我将在本文中讨论的构建器模式**与 GOF“设计模式”书中提到的**稍有不同。 这本书说: 构建器模式是一种设计模式,它允许使用正确的操作顺序逐步创建复杂的对象。 构造由导向对象控制,该导向对象仅需要知道要创建的对象的类型。 本书给出如下示例: ![Builder Pattern](https://img.kancloud.cn/dc/4d/dc4d3f66309e2d42987ad1fba236845f_500x171.png) 我真的很难在现实生活中的编程和应用中利用以上示例。 上面的过程与[**抽象工厂**](//howtodoinjava.com/design-patterns/creational/abstract-factory-pattern-in-java/ "Abstract factory pattern in java")模式非常相似(并非完全相似),在该模式中,您找到了特定类型对象的工厂(或构建器),然后工厂为您提供了具体的 该对象的实例。 **这个构建器模式与抽象工厂模式之间唯一的不同之处在于,构建器为您提供了对对象创建过程的更多控制,仅此而已。 除此之外,没有重大差异。** 一句话,抽象工厂模式是对“`WHAT`”的回答,而构建器模式是对“`HOW`”的回答。 对我来说,构建器模式更像[**流利的接口**](https://en.wikipedia.org/wiki/Fluent_interface "Fluent_interface") 。 通常使用**方法级联**(或方法链接)来实现流利的接口。 现在,从这里开始,我将以我认为在实际情况下特别有用的方式来讨论构建器模式。 我也希望说服你们。 ```java Sections in this post: Definition of Builder Pattern Where we need Builder Pattern A sample implementation using Builder Pattern Existing implementations in JDK Benefits and Advantages Costs and Disadvantages Conclusion ``` ## 构建器模式的定义 首先,给构建器模式一个定义: > 构建器模式的目标是“将复杂对象的构造与其表示分开,以便同一构造过程可以创建不同的表示。” ## 我们需要构建器模式的地方 我们已经知道[**不可变**](//howtodoinjava.com/java/related-concepts/how-to-make-a-java-class-immutable/ "How to make a java class immutable")和不可变实例在应用中的好处。 如果对此有任何疑问,请让我想起 Java 中的`String`类。 正如我已经说过的那样,构建器模式可以帮助我们创建具有大量状态属性的不可变类。 让我们讨论一下我们应用中的一个常见问题。 假设在任何用户管理模块中,主要实体都是`User`。 理想情况下,实际上,一旦完全创建了用户对象,您就不想更改其状态。 根本没有道理,对吧? 现在,假设我们的`User`对象具有以下 5 个属性,即`firstName`,`lastName`,`age`,`phone`和`address`。 在通常的实践中,如果要创建不可变的`User`类,则必须将所有五个信息作为参数传递给构造器。 它看起来像这样: ```java public User (String firstName, String lastName, int age, String phone, String address){ this.firstName = firstName; this.lastName = lastName; this.age = age; this.phone = phone; this.address = address; } ``` 很好。 现在,如果只有`firstName`和`lastName`是必不可少的,剩下的 3 个字段是可选的,该怎么办。 问题! 我们需要更多的构造器。 ```java public User (String firstName, String lastName, int age, String phone){ ... } public User (String firstName, String lastName, String phone, String address){ ... } public User (String firstName, String lastName, int age){ ... } public User (String firstName, String lastName){ ... } ``` 我们将需要更多类似上面的内容。 还能管理吗? 现在,让我们介绍我们的第六个属性,即薪水。 现在是问题。 *一种创建更多构造器的方法,另一种方法是放松不变性并引入设置器方法。 您选择这两个选项中的任何一个,就松了点,对吧?* 在这里,构建器模式将帮助您使用其他属性,同时保留 Use 类的不变性。 ## 使用构建器模式的示例实现 下面是我们上面讨论的问题的编码解决方案。 这使用附加的类`UserBuilder`,该类可帮助我们构建具有所有必需属性和可选属性组合的所需`User`对象,而不会失去不变性。 ```java public class User { //All final attributes private final String firstName; // required private final String lastName; // required private final int age; // optional private final String phone; // optional private final String address; // optional private User(UserBuilder builder) { this.firstName = builder.firstName; this.lastName = builder.lastName; this.age = builder.age; this.phone = builder.phone; this.address = builder.address; } //All getter, and NO setter to provde immutability public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public int getAge() { return age; } public String getPhone() { return phone; } public String getAddress() { return address; } @Override public String toString() { return "User: "+this.firstName+", "+this.lastName+", "+this.age+", "+this.phone+", "+this.address; } public static class UserBuilder { private final String firstName; private final String lastName; private int age; private String phone; private String address; public UserBuilder(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public UserBuilder age(int age) { this.age = age; return this; } public UserBuilder phone(String phone) { this.phone = phone; return this; } public UserBuilder address(String address) { this.address = address; return this; } //Return the finally consrcuted User object public User build() { User user = new User(this); validateUserObject(user); return user; } private void validateUserObject(User user) { //Do some basic validations to check //if user object does not break any assumption of system } } } ``` 下面是方法,我们将在代码中使用`UserBuilder`: ```java public static void main(String[] args) { User user1 = new User.UserBuilder("Lokesh", "Gupta") .age(30) .phone("1234567") .address("Fake address 1234") .build(); System.out.println(user1); User user2 = new User.UserBuilder("Jack", "Reacher") .age(40) .phone("5655") //no address .build(); System.out.println(user2); User user3 = new User.UserBuilder("Super", "Man") //No age //No phone //no address .build(); System.out.println(user3); } Output: User: Lokesh, Gupta, 30, 1234567, Fake address 1234 User: Jack, Reacher, 40, 5655, null User: Super, Man, 0, null, null ``` 请注意,上面创建的用户对象**没有任何设置方法**,因此一旦建立,便无法更改其状态。 这提供了所需的不变性。 有时,开发人员将属性添加到`User`类时,可能会忘记为构建器添加对新属性的支持。 为了最大程度地减少这种情况,我们应该将构建器封装在它们所构建的类中(如上例所示),以使开发人员更清楚地知道也需要更新一个相关的构建器。 > 有时,我认为应该有一个**驱逐舰模式**(与构建器相对),该模式应该以系统的方式从复杂对象中拆除某些属性。 你怎么看? ## JDK 中的现有实现 [`java.lang.Appendable`](https://docs.oracle.com/javase/7/docs/api/java/lang/Appendable.html "Appendable") 的所有实现实际上都是在 Java 中使用 Builder 模式的良好示例。 例如: [`java.lang.StringBuilder#append()`](https://docs.oracle.com/javase/7/docs/api/java/lang/StringBuilder.html#append%28java.lang.CharSequence%29 "StringBuilder") [未同步的类] [`java.lang.StringBuffer#append()`](https://docs.oracle.com/javase/1.5.0/docs/api/java/lang/StringBuffer.html#append%28java.lang.CharSequence%29 "StringBuffer") [同步类] [`java.nio.ByteBuffer#put()`](https://docs.oracle.com/javase/6/docs/api/java/nio/ByteBuffer.html#put%28java.nio.ByteBuffer%29 "ByteBuffer")(也在 CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer 和 DoubleBuffer 上) 另一种用法可以在[`javax.swing.GroupLayout.Group#addComponent()`](https://docs.oracle.com/javase/6/docs/api/javax/swing/GroupLayout.Group.html#addComponent%28java.awt.Component%29 "GroupLayout")中找到。 看看这些实现与我们上面讨论的相似。 ```java StringBuilder builder = new StringBuilder("Temp"); String data = builder.append(1) .append(true) .append("friend") .toString(); System.out.println(data); Output: Temp1truefriend ``` ## 构建器模式的优点和好处 毫无疑问,在构建器模式中,**代码行数增加了**至少增加了一倍,但是在**设计灵活性**和更多**可读代码**方面,努力得到了回报 。 构造器的**参数被简化**,并在**高可读性方法调用**中提供。 构建器模式还有助于最大程度地减少构造器中的参数数量,因此**不需要将可选参数**的`null`传递给构造器。 真的吸引了我 另一个好处是,对象总是以`complete`状态实例化,而不是处于不完整的状态,直到开发人员调用(如果曾经调用)适当的设置器方法来设置其他字段。 最后,我可以在对象构建过程中构建**不可变对象**,而无需太多复杂的逻辑。 ## 构建器模式的成本与缺点 尽管构建器模式减少了一些代码行,从而消除了使用设置器方法的需要,但仍然**通过引入构建器对象使总行数**翻了一番。 此外,尽管客户代码更具可读性,但客户代码也更为冗长。 虽然对我而言,可读性比代码行更重要。 我能想到的唯一缺点。 仅此而已。 如果您也分享您的想法,我会很高兴。 **祝您学习愉快!** 参考文献 [http://en.wikipedia.org/wiki/Builder_pattern](https://en.wikipedia.org/wiki/Builder_pattern) [http://www.javaspecialists.eu/archive/Issue163.html](http://www.javaspecialists.eu/archive/Issue163.html) [http://en.wikipedia.org/wiki/Fluent_interface](https://en.wikipedia.org/wiki/Fluent_interface) [http://martinfowler.com/bliki/FluentInterface.html](http://martinfowler.com/bliki/FluentInterface.html)