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

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 构建者模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/346354.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

webgis从基础到开发实践_ArcGIS API For Javascript 开发笔记(四)

二、应用篇1、应用部署部署也就意味着一个 DEMO 或者系统即将完工,也意味着系统即将上线,相对来说Javascript 应用的部署不是很复杂,但是这是有前提的,要对部署中的一些概念有所了解,比如部署中常常提到的虚拟目录&…

Spock 1.2 –轻松进行集成测试中的Spring Bean模拟

探索如何使用Spock 1.2将Spock的模拟和间谍自动注入到Spring上下文中。 Spock中的存根/模拟/间谍(及其生命周期)一直与Spock Specification类紧密结合。 只能在测试类中创建它们。 因此,使用共享的预定义模拟(在单元测试和集成测…

geteditor p 取消自动_2020百度网盘超级会员怎么取消自动续费?

首先我们打开百度网盘,开通超级会员以后,可以看到个人中心有一个管理自动续费的,点击打开。2然后可以看到自己开通的界面旁边显示的有一个取消按钮。3点击取消以后,会提示我们自动续费享受的有优惠,我们继续点击确认取…

java堆和客栈_java中堆和栈的区别分析

堆和栈是java数据结构里非常重要的概念,本文较为详细的分析了二者之间的区别。供大家参考。具体如下:Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarr栈的优势是,存取速度比堆要快&a…

php odbc驱动,用于Windows的PHP 7.0 ODBC驱动程序

我将PHP 5.6.30(https://www.apachefriends.org/de/download.html)升级到PHP 7.0(https://bitnami.com/stack/wamp/installer)到目前为止,一切都运行良好,当我使用MySQL数据库时,它将我的页面的加载时间从1,2秒减少到约300毫秒.但是现在我正在尝试使用以下简单脚本连接到MSSQL数…

分布式机器学习_229页,CMU博士张昊毕业论文公布,探索机器学习并行化的奥秘...

CMU 机器人研究所张昊(Hao Zhang)博士论文新鲜出炉,主要围绕着机器学习并行化的自适应、可组合与自动化问题展开。机器之心报道,机器之心编辑部。随着近年来,机器学习领域的创新不断加速,SysML 的研究者已经…

利用Excel或LibreOffice的业务输入进行单元测试约束

业务专家向我们(开发人员)解释业务限制。 但是,如何确保我们正确理解它们呢? 或更糟糕的是,一旦约束条件正式化,我们如何测试他们之间是否同意? 好吧,有一种很棒的方法可以做到这一…

Java播放歌词同步源码,Android 实现歌词同步

歌词同步的原理其实很简单:歌词显示是一个线程, 音乐播放时一个线程,通过播放时间将歌词显示进度与播放进度同步起来。网络标准的歌词格式是LRC。 我们看下一个LRC文档,其格式就一目了然了。[ti:爱][ar:小虎…

矩阵连乘 动态规划_Java动态规划

1. 介绍动态规划典型的被用于优化递归算法,因为它们倾向于以指数的方式进行扩展。动态规划主要思想是将复杂问题(带有许多递归调用)分解为更小的子问题,然后将它们保存到内存中,这样我们就不必在每次使用它们时重新计算它们。要理解动态规划的…

c php乱码,php分割GBK中文乱码的解决方法

类似如下的字符串(GBK), explode不能得到正确结果:$result explode("|", "滕华弢|海青");究其原因, 对于”弢”字(读tao,不认识没关系,我也不认识), 因为他的GBK编码值为: 8f7c, 不巧的是, “|”的ASCII值也是7c.还有一些类似的问题: 因为GBK编码的编码范…

分布式光伏补贴_光伏发电上网电价政策综述

原标题:光伏发电上网电价政策综述近些年,光伏发电上网电价相关政策不断推陈出新,在推进平价上网的同时,也积极对接了电力市场建设需要。本文梳理了光伏发电上网电价及补贴标准有关政策,供参考使用:1.普通光…

怎么简单的锁定文件夹_简单性与鲁棒性–在锁定文件处理中展示

怎么简单的锁定文件夹今天,我们将讨论使事情保持简单,愚蠢(KISS)和鲁棒性的设计价值之间,设计不足和过度设计之间的冲突。 我们正在编写一个批处理Java应用程序,需要确保在服务器上一次最多运行一个实例。…

php strlen 线程安全,浅析PHP中strlen和mb_strlen的区别

在PHP中,strlen与mb_strlen是求字符串长度的函数,但是对于一些初学者来说,如果不看手册,也许不太清楚其中的区别。下面通过例子,讲解这两者之间的区别。先看例子://测试时文件的编码方式要是UTF8$str中文a字…

单片机float数发给上位机_上位机倒计时器

1. 设置窗口在VS2019新建一个Windows窗体应用(.NET Framework)项目,新建之后出现一个可视化页面(From1.cs[设计])设置窗口。在左侧的工具箱里面拉拽控件到操作面板上,按照自己的喜好摆放位置和调大小。这里要注意的是用到的分别是什么控件:文…

JDK语言功能预览:切换表达式

JEP 12 [“预览语言和VM功能”]在其主页上描述如下: 预览语言或VM功能是Java SE平台的一项新功能,该功能已完全指定,完全实现但不是永久性的。 JDK功能发布中提供了该功能,以根据实际使用情况激发开发人员反馈。 这可能会导致它在…

python快速示例_Python编程入门-基本示例,快速,上,手,基础

环境配置安装Python,目前最新的是3.9(不支持Windows7),我装的3.7https://www.python.org/downloads/安装向导里勾选自动添加路径,装完就可以用,Windows10/7搜索框输入IDLE,进入交互式环境窗口,输入语句后&a…

php 距离排序,php 附近由近到远排序查询

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼//获取经纬度 默认值为0.5千米function returnSquarePoint($lng, $lat, $distance 0.5){define(EARTH_RADIUS, 6371); //地球半径,平均半径为6371km$dlng 2 * asin(sin($distance / (2 * EARTH_RADIUS)) / cos(deg2ra…

土地利用覆被变化的概念_欠压实概念极其荒谬

●李传亮地表疏松的沉积物,孔隙度极高,有时可高达80%以上,但随着埋藏深度的加大,在上覆压力的作用下,其排列方式不断由松散排列趋于致密排列,同时伴随有孔隙度的大幅度减小,此即岩石的压实阶段&…

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

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

java足球游戏毕业设计,java毕业设计_springboot框架的校园足球管理平台

今天介绍一个java毕设题目, 题目内容为springboot框架的校园足球管理平台, 是一个采用b/s结构的javaweb项目, 采用java语言编写开发工具eclipse, 项目框架jspspringbootmybatis, 校园足球管理平台的信息存储于mysql中, 并基于mybatis进行了orm封装, 该校园足球管理平台系统通过…