java 构建者模式

在我的前两篇文章中,我研究了如何通过自定义类型和参数对象减少构造函数或方法调用所需的参数数量。 在本文中,我将讨论如何使用构建器模式来减少构造器所需的参数数量,并讨论该模式如何甚至可以帮助采用过多参数的非构造器方法。

在第二版的有效Java中 , Josh Bloch 在项目#2中引入了使用构建器模式来处理需要太多参数的构造器。 Bloch不仅演示了如何使用Builder,而且还解释了它比接受大量参数的构造函数更具优势。 我将在本文的结尾处获得这些优点,但认为必须指出,Bloch在他的书中将整个项目专门用于这种做法。

为了说明这种方法的优点,我将使用以下示例Person类。 它没有我通常会添加到此类的所有方法,因为我想专注于其构造。

Person.java(无构建器模式)

package dustin.examples;/*** Person class used as part of too many parameters demonstration.* * @author Dustin*/
public class Person
{private final String lastName;private final String firstName;private final String middleName;private final String salutation;private final String suffix;private final String streetAddress;private final String city;private final String state;private final boolean isFemale;private final boolean isEmployed;private final boolean isHomewOwner;public Person(final String newLastName,final String newFirstName,final String newMiddleName,final String newSalutation,final String newSuffix,final String newStreetAddress,final String newCity,final String newState,final boolean newIsFemale,final boolean newIsEmployed,final boolean newIsHomeOwner){this.lastName = newLastName;this.firstName = newFirstName;this.middleName = newMiddleName;this.salutation = newSalutation;this.suffix = newSuffix;this.streetAddress = newStreetAddress;this.city = newCity;this.state = newState;this.isFemale = newIsFemale;this.isEmployed = newIsEmployed;this.isHomewOwner = newIsHomeOwner;}
}

此类的构造函数可以工作,但是客户端代码很难正确使用。 可以使用Builder模式使构造函数更易于使用。 正如我之前所写的那样, NetBeans将为我重构此内容 。 接下来显示重构代码的示例(NetBeans通过创建所有新的Builder类来完成此操作)。

PersonBuilder.java

package dustin.examples;public class PersonBuilder
{private String newLastName;private String newFirstName;private String newMiddleName;private String newSalutation;private String newSuffix;private String newStreetAddress;private String newCity;private String newState;private boolean newIsFemale;private boolean newIsEmployed;private boolean newIsHomeOwner;public PersonBuilder(){}public PersonBuilder setNewLastName(String newLastName) {this.newLastName = newLastName;return this;}public PersonBuilder setNewFirstName(String newFirstName) {this.newFirstName = newFirstName;return this;}public PersonBuilder setNewMiddleName(String newMiddleName) {this.newMiddleName = newMiddleName;return this;}public PersonBuilder setNewSalutation(String newSalutation) {this.newSalutation = newSalutation;return this;}public PersonBuilder setNewSuffix(String newSuffix) {this.newSuffix = newSuffix;return this;}public PersonBuilder setNewStreetAddress(String newStreetAddress) {this.newStreetAddress = newStreetAddress;return this;}public PersonBuilder setNewCity(String newCity) {this.newCity = newCity;return this;}public PersonBuilder setNewState(String newState) {this.newState = newState;return this;}public PersonBuilder setNewIsFemale(boolean newIsFemale) {this.newIsFemale = newIsFemale;return this;}public PersonBuilder setNewIsEmployed(boolean newIsEmployed) {this.newIsEmployed = newIsEmployed;return this;}public PersonBuilder setNewIsHomeOwner(boolean newIsHomeOwner) {this.newIsHomeOwner = newIsHomeOwner;return this;}public Person createPerson() {return new Person(newLastName, newFirstName, newMiddleName, newSalutation, newSuffix, newStreetAddress, newCity, newState, newIsFemale, newIsEmployed, newIsHomeOwner);}}

我更喜欢将Builder作为嵌套类在其要构建其对象的类中,但是NetBeans自动生成独立的Builder很容易使用。 NetBeans生成的生成器与我要编写的生成器之间的另一个区别是,我首选的生成器实现在生成器的构造函数中提供了必填字段,而不是提供了无参数的构造函数。 下一个代码清单从上方显示了我的Person类,并在其中添加了一个Builder作为嵌套类。

具有嵌套Person.Builder的Person.java

package dustin.examples;/*** Person class used as part of too many parameters demonstration.* * @author Dustin*/
public class Person
{private final String lastName;private final String firstName;private final String middleName;private final String salutation;private final String suffix;private final String streetAddress;private final String city;private final String state;private final boolean isFemale;private final boolean isEmployed;private final boolean isHomewOwner;public Person(final String newLastName,final String newFirstName,final String newMiddleName,final String newSalutation,final String newSuffix,final String newStreetAddress,final String newCity,final String newState,final boolean newIsFemale,final boolean newIsEmployed,final boolean newIsHomeOwner){this.lastName = newLastName;this.firstName = newFirstName;this.middleName = newMiddleName;this.salutation = newSalutation;this.suffix = newSuffix;this.streetAddress = newStreetAddress;this.city = newCity;this.state = newState;this.isFemale = newIsFemale;this.isEmployed = newIsEmployed;this.isHomewOwner = newIsHomeOwner;}public static class PersonBuilder{private String nestedLastName;private String nestedFirstName;private String nestedMiddleName;private String nestedSalutation;private String nestedSuffix;private String nestedStreetAddress;private String nestedCity;private String nestedState;private boolean nestedIsFemale;private boolean nestedIsEmployed;private boolean nestedIsHomeOwner;public PersonBuilder(final String newFirstName,final String newCity,final String newState) {this.nestedFirstName = newFirstName;this.nestedCity = newCity;this.nestedState = newState;}public PersonBuilder lastName(String newLastName){this.nestedLastName = newLastName;return this;}public PersonBuilder firstName(String newFirstName){this.nestedFirstName = newFirstName;return this;}public PersonBuilder middleName(String newMiddleName){this.nestedMiddleName = newMiddleName;return this;}public PersonBuilder salutation(String newSalutation){this.nestedSalutation = newSalutation;return this;}public PersonBuilder suffix(String newSuffix){this.nestedSuffix = newSuffix;return this;}public PersonBuilder streetAddress(String newStreetAddress){this.nestedStreetAddress = newStreetAddress;return this;}public PersonBuilder city(String newCity){this.nestedCity = newCity;return this;}public PersonBuilder state(String newState){this.nestedState = newState;return this;}public PersonBuilder isFemale(boolean newIsFemale){this.nestedIsFemale = newIsFemale;return this;}public PersonBuilder isEmployed(boolean newIsEmployed){this.nestedIsEmployed = newIsEmployed;return this;}public PersonBuilder isHomeOwner(boolean newIsHomeOwner){this.nestedIsHomeOwner = newIsHomeOwner;return this;}public Person createPerson(){return new Person(nestedLastName, nestedFirstName, nestedMiddleName,nestedSalutation, nestedSuffix,nestedStreetAddress, nestedCity, nestedState,nestedIsFemale, nestedIsEmployed, nestedIsHomeOwner);}}
}

当通过使用自定义类型和参数对象进行增强时,Builder甚至会变得更好,正如我在“参数过多”问题的前两篇文章中概述的那样。 这显示在下一个代码清单中。

具有嵌套生成器,自定义类型和参数对象的Person.java

package dustin.examples;/*** Person class used as part of too many parameters demonstration.* * @author Dustin*/
public class Person
{private final FullName name;private final Address address;private final Gender gender;private final EmploymentStatus employment;private final HomeownerStatus homeOwnerStatus;/*** Parameterized constructor can be private because only my internal builder* needs to call me to provide an instance to clients.* * @param newName Name of this person.* @param newAddress Address of this person.* @param newGender Gender of this person.* @param newEmployment Employment status of this person.* @param newHomeOwner Home ownership status of this person.*/private Person(final FullName newName, final Address newAddress,final Gender newGender, final EmploymentStatus newEmployment,final HomeownerStatus newHomeOwner){this.name = newName;this.address = newAddress;this.gender = newGender;this.employment = newEmployment;this.homeOwnerStatus = newHomeOwner;}public FullName getName(){return this.name;}public Address getAddress(){return this.address;}public Gender getGender(){return this.gender;}public EmploymentStatus getEmployment(){return this.employment;}public HomeownerStatus getHomeOwnerStatus(){return this.homeOwnerStatus;}/*** Builder class as outlined in the Second Edition of Joshua Bloch's* Effective Java that is used to build a {@link Person} instance.*/public static class PersonBuilder{private FullName nestedName;private Address nestedAddress;private Gender nestedGender;private EmploymentStatus nestedEmploymentStatus;private HomeownerStatus nestedHomeOwnerStatus;public PersonBuilder(final FullName newFullName,final Address newAddress) {this.nestedName = newFullName;this.nestedAddress = newAddress;}public PersonBuilder name(final FullName newName){this.nestedName = newName;return this;}public PersonBuilder address(final Address newAddress){this.nestedAddress = newAddress;return this;}public PersonBuilder gender(final Gender newGender){this.nestedGender = newGender;return this;}public PersonBuilder employment(final EmploymentStatus newEmploymentStatus){this.nestedEmploymentStatus = newEmploymentStatus;return this;}public PersonBuilder homeOwner(final HomeownerStatus newHomeOwnerStatus){this.nestedHomeOwnerStatus = newHomeOwnerStatus;return this;}public Person createPerson(){return new Person(nestedName, nestedAddress, nestedGender,nestedEmploymentStatus, nestedHomeOwnerStatus);}}
}

最后两对代码清单显示了通常如何使用Builder-构造对象。 实际上, 约书亚·布洛赫 ( Joshua Bloch)的 《 有效Java 》 第二版中关于构建器的项目(项目2)在创建(和销毁)对象的章节中。 但是,构建器可以通过允许更简单的方法来构建传递给方法的参数对象,从而间接地帮助非构造方法。

例如,在最后一个代码清单中,方法已将一些参数对象( FullNameAddress )传递给它们。 客户端必须构造这些参数对象可能很乏味,并且可以使用构建器来使该过程减少繁琐。 因此,尽管在每种情况下都使用构建器进行构造,但通过更轻松地使用减少方法自变量计数的参数对象,它间接地使非构造器方法受益。

接下来显示将用作参数对象的FullNameAddress类的新定义,以及使用Builder本身。

带有Builder的FullName.java

package dustin.examples;/*** Full name of a person.* * @author Dustin*/
public final class FullName
{private final Name lastName;private final Name firstName;private final Name middleName;private final Salutation salutation;private final Suffix suffix;private FullName(final Name newLastName,final Name newFirstName,final Name newMiddleName,final Salutation newSalutation,final Suffix newSuffix){this.lastName = newLastName;this.firstName = newFirstName;this.middleName = newMiddleName;this.salutation = newSalutation;this.suffix = newSuffix;}public Name getLastName(){return this.lastName;}public Name getFirstName(){return this.firstName;}public Name getMiddleName(){return this.middleName;}public Salutation getSalutation(){return this.salutation;}public Suffix getSuffix(){return this.suffix;}@Overridepublic String toString(){return  this.salutation + " " + this.firstName + " " + this.middleName+ this.lastName + ", " + this.suffix;}public static class FullNameBuilder{private final Name nestedLastName;private final Name nestedFirstName;private Name nestedMiddleName;private Salutation nestedSalutation;private Suffix nestedSuffix;public FullNameBuilder(final Name newLastName, final Name newFirstName){this.nestedLastName = newLastName;this.nestedFirstName = newFirstName;}public FullNameBuilder middleName(final Name newMiddleName){this.nestedMiddleName = newMiddleName;return this;}public FullNameBuilder salutation(final Salutation newSalutation){this.nestedSalutation = newSalutation;return this;}public FullNameBuilder suffix(final Suffix newSuffix){this.nestedSuffix = newSuffix;return this;}public FullName createFullName(){return new FullName(nestedLastName, nestedFirstName, nestedMiddleName,nestedSalutation, nestedSuffix);}}
}

使用Builder的Address.java

package dustin.examples;/*** Representation of a United States address.* * @author Dustin*/
public final class Address
{private final StreetAddress streetAddress;private final City city;private final State state;private Address(final StreetAddress newStreetAddress, final City newCity, final State newState){this.streetAddress = newStreetAddress;this.city = newCity;this.state = newState;}public StreetAddress getStreetAddress(){return this.streetAddress;}public City getCity(){return this.city;}public State getState(){return this.state;}@Overridepublic String toString(){return this.streetAddress + ", " + this.city + ", " + this.state;}public static class AddressBuilder{private StreetAddress nestedStreetAddress;private final City nestedCity;private final State nestedState;public AddressBuilder(final City newCity, final State newState){this.nestedCity = newCity;this.nestedState = newState;}public AddressBuilder streetAddress(final StreetAddress newStreetAddress){this.nestedStreetAddress = newStreetAddress;return this;}public Address createAddress(){return new Address(nestedStreetAddress, nestedCity, nestedState);}}
}

通过将上述构建器包含在类中,可以创建一个Person实例,如下面的代码清单所示。 之后显示一个更传统的Person实例实例以进行比较。

客户代码实例化带有构建器的Person两个示例

final Person person1 = new Person.PersonBuilder(new FullName.FullNameBuilder(new Name("Dynamite"), new Name("Napoleon")).createFullName(),new Address.AddressBuilder(new City("Preston"), State.ID).createAddress()).createPerson();final Person person2 = new Person.PersonBuilder(new FullName.FullNameBuilder(new Name("Coltrane"), new Name("Rosco")).middleName(new Name("Purvis")).createFullName(),new Address.AddressBuilder(new City("Hazzard"), State.GA).createAddress()).gender(Gender.MALE).employment(EmploymentStatus.EMPLOYED).createPerson();

实例化没有构建器的人

final person = new Person("Coltrane", "Rosco", "Purvis", null, "Hazzard", "Georgia", false, true, true);

如先前的代码片段所示,与使用构建器类相比,用于调用传统Java构造函数的客户端代码远不易读且易于弄乱。 相同类型的多种(字符串和布尔值)以及在构造函数中对可选属性的调用中放置空值的必要性,为该方法的严重失败提供了许多方法。

优势与优势

Builder模式的成本很高,因为每个模式本质上必须将代码行数加倍,并且用于设置这些属性。 但是,当客户代码在可用性和可读性方面大大受益时,这个代价是值得的。 构造函数的参数会减少,并以高度可读的方法调用提供。

Builder方法的另一个优点是能够通过使用“ set”方法在单个语句和状态下获取对象而不会出现多个状态下的对象的问题。 我越来越意识到在多核世界中不变性的价值,当该类具有大量属性时, Builder模式非常适合该不变类。 我也喜欢不需要为可选参数传递null到构造函数。

Builder模式不仅使代码更具可读性,而且使应用IDE的代码完成功能更加容易。 在有效Java第二版的第2条中概述了与构建器一起使用时,构建器模式的其他优点。

成本与劣势

如上所示和上面提到的,对于使用builder方法的 “ set”方法,给定类的代码行数必须实质上增加一倍。 此外,尽管客户代码更具可读性,但客户代码也更为冗长。 我认为,随着参数数量的增加或更多参数共享同一类型,或者随着可选参数数量的增加,更高的可读性值得付出代价。

带有构建器的类中的代码行有时意味着开发人员将属性添加到主类时,可能会忘记为构建器添加对新属性的支持。 为了尝试解决这个问题,我喜欢将构建器嵌套在他们构建的类中,这样对于开发人员来说,很明显,有一个相关的构建器需要进行类似的更新。 尽管仍然存在开发人员忘记为构建器添加对新属性的支持的风险,但这与忘记将新属性添加到类的toString() , equals(Object) , hashCode()的风险没有什么不同。或通常基于类的所有属性的其他方法。

在实现Builder的过程中 ,我使客户端将必需的属性传递给了Builder的构造函数,而不是通过“ set”方法。 这样做的好处是,在开发人员调用(如果曾经调用过)适当的“ set”方法来设置其他字段之前,对象总是以“完成”状态实例化,而不是处于未完成状态。 这对于享受不变性的好处是必要的。 但是,这种方法的一个次要缺点是,我没有获得为要设置的字段命名的方法的可读性优点。

顾名思义,Builder实际上只是构造函数的替代方案,并不直接用于减少非构造函数方法参数的数量。 但是,可以将构建器与参数对象结合使用,以减少非构造器方法参数的数量。 可以在有关“ 深入研究Builder模式 ” 一文中的评论中找到反对使用Builder进行对象构造的其他论点。

结论

当我有很多参数时,尤其是当其中许多参数为null且其中许多参数共享同一数据类型时,我真的很喜欢使用Builder模式构造对象。 开发人员可能会认为,用于实现Builder的额外代码可能无法证明其对于少量参数的好处,尤其是在需要少量参数且类型不同的情况下。 在这种情况下,使用传统的构造函数可能是合乎需要的,或者,如果不需要不变性,则使用无参数的构造函数,并要求客户端知道调用必要的“设置”方法是合乎需要的。

参考: Java方法中的参数太多,第3部分: JCG合作伙伴 Dustin Marx的“ Instanted by Actual Events”博客中的构建器模式 。

翻译自: https://www.javacodegeeks.com/2013/10/too-many-parameters-in-java-methods-part-3-builder-pattern.html

java 构建者模式

java 构建者模式_Java方法中的参数太多,第3部分:构建器模式相关推荐

  1. java命令行参数工具_Java方法中的参数太多,第8部分:工具

    java命令行参数工具 在我的系列文章的前七篇文章中,有关处理Java方法中期望的参数过多的内容集中在减少方法或构造函数期望的参数数量的替代方法上. 在本系列的第八篇文章中,我将介绍一些工具,这些工具 ...

  2. java中重载 参数顺序_Java方法中的参数太多,第4部分:重载

    java中重载 参数顺序 期望将过多的参数传递给Java方法的问题之一是,该方法的客户端很难确定它们是否以适当的顺序传递了适当的值. 在以前的文章中,我描述了如何使用自定义类型 , 参数对象和构建器来 ...

  3. java 可变参数方法_Java方法中的参数太多,第7部分:可变状态

    java 可变参数方法 在我的系列文章的第七篇中,有关解决Java方法或构造函数中过多参数的问题 ,我着眼于使用状态来减少传递参数的需要. 我等到本系列的第七篇文章来解决这个问题的原因之一是,它是我最 ...

  4. java方法带参数返回值_Java方法中的参数太多,第6部分:方法返回

    java方法带参数返回值 在当前的系列文章中,我正在致力于减少调用Java方法和构造函数所需的参数数量,到目前为止,我一直专注于直接影响参数本身的方法( 自定义类型 , 参数对象 , 构建器模式 , ...

  5. java自定义方法参数注解_Java方法中的参数太多,第1部分:自定义类型

    java自定义方法参数注解 我认为构造函数和方法中冗长的参数列表是Java开发中的另一个" 危险信号 ",就逻辑和功能而言,它们不一定是"错误的",但通常暗示当 ...

  6. java中方法的命名_Java方法中的参数太多,第5部分:方法命名

    java中方法的命名 在上一篇文章 (有关处理Java方法中过多参数的系列文章的 第4部分 )中,我将方法重载视为一种向客户提供需要较少参数的版本的方法或构造函数的方法. 我描述了该方法的一些缺点,并 ...

  7. java方法传对象参数_Java方法中的参数太多,第2部分:参数对象

    java方法传对象参数 在上一篇文章中 ,我研究了与方法和构造函数的长参数列表相关的一些问题. 在那篇文章中,我讨论了用自定义类型替换基元和内置类型以提高可读性和类型安全性. 这种方法使方法或构造函数 ...

  8. Java方法中的参数太多,第3部分:构建器模式

    在我的前两篇文章中,我研究了如何通过自定义类型和参数对象减少构造函数或方法调用所需的参数数量. 在本文中,我将讨论如何使用构建器模式来减少构造器所需的参数数量,并讨论该模式如何甚至可以帮助采用过多参数 ...

  9. Java方法中的参数太多,第8部分:工具

    在我的系列文章的前七篇文章中,有关处理Java方法中期望的参数过多的内容集中在减少方法或构造函数期望的参数数量的替代方法上. 在本系列的第八篇文章中,我将介绍一些工具,这些工具可帮助您确定可能存在过多 ...

最新文章

  1. 真能一快遮百丑?为什么要弃坑FastJson
  2. Nginx配置段(3)
  3. Swift 3 新特性
  4. java例7_在Java 7中处理周数
  5. 为什么 Java 中 2*(i*i) 比 2*i*i 更快?
  6. php java session共享_PHP实现session共享
  7. 《Java程序设计》第2周学习总结
  8. 用vscode可以开发php,【编程开发工具】vscode能够编写php吗
  9. 2021当代青年婚恋状态研究报告
  10. clock_gettime接口和linux时间系统
  11. VS2017 CUDA编程学习10:纹理内存
  12. 内连接和外连接的区别--举例
  13. java-FFmpeg(一) 实现视频的转码和截图功能
  14. Application.DoEvents()那些事
  15. 【面试】阿里巴巴Java经典面试题整理及答案详解
  16. HTML+CSS+JS 制作简单动漫网页
  17. Samba服务器的配置全过程
  18. 微信升级外链管理规范,「砍一刀帮我加速」要被禁止了
  19. android开发截屏代码,android截屏代码:C++实现
  20. 火狐浏览器插件开发小试

热门文章

  1. Summer Training day6 coseforces339D 线段树、位操作
  2. JavaFX官方教程(十四)之转换,动画和视觉效果教程的源代码
  3. JavaFX官方教程(十三)之应用效果
  4. 启动tomcat不出现命令窗口
  5. 13-一对多左连接查询分步查询(查询所有客户及客户对应的订单)
  6. git创建tag标签
  7. 经典指针程序互换(一)
  8. React中antd的按需引入+自定主题
  9. Opencv FFmpeg Ubuntu下编译问题
  10. js 方法传递对象参数