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

java 可变参数方法

在我的系列文章的第七篇中,有关解决Java方法或构造函数中过多参数的问题 ,我着眼于使用状态来减少传递参数的需要。 我等到本系列的第七篇文章来解决这个问题的原因之一是,它是我最不喜欢的减少传递给方法和构造函数的参数的方法之一。 也就是说,这种方法有多种风味,我绝对更喜欢某些风味。

在所有软件开发中,使用状态减少参数的方法中最著名,最被嘲笑的方法可能是使用全局变量 。 尽管从语义上讲Java没有全局变量可能是准确的,但事实是,无论是好是坏,都可以通过公共静态构造在Java中实现全局变量的等效项 。 在Java中实现此目标的一种特别流行的方法是通过有状态的Singleton 。

马丁·福勒(Martin Fowler)在《企业应用程序架构模式》中写道:“任何全球数据在被证明无辜之前总是有罪的。” 出于多种原因 ,Java中的全局变量和“类全局”构造被认为是不好的形式。 它们会使开发人员难以维护和读取代码来知道值的定义位置,最后更改甚至是来源。 就其本质和意图而言,全局数据违反了封装和数据隐藏的原理。

MiškoHevery 用面向对象的语言编写了有关静态全局变量问题的以下文章:


静态访问全局状态不会向使用全局状态的构造函数和方法的读者阐明那些共享的依赖关系。 Global State和Singletons使API依赖其真正的依赖关系。 …全局状态的根本问题是它可以全局访问。 在理想情况下,一个对象应该只能与直接传递给其他对象(通过构造函数或方法调用)的其他对象进行交互。

具有全局可用状态减少了对参数的需求,因为如果两个对象已经可以直接访问该数据,则无需将其传递给另一个对象。 但是,正如Hevery所说,这完全与面向对象设计的意图正交。

随着并发应用变得越来越普遍,可变状态也是一个日益严重的问题。 在他关于Scala的JavaOne 2012演示中 , Scala的创建者Martin Odersky指出,在高度并发的世界中“拥有的每一个可变状态都是一种责任”,并补充说问题是“由并发线程访问共享可变状态导致的不确定性。 ”

尽管有避免避免可变状态的原因,但是它仍然是软件开发中普遍使用的方法。 我认为这样做的原因有很多,其中包括编写可变状态共享代码非常容易,可变共享代码的确提供了访问的便利。 某些类型的可变数据很受欢迎,因为这些类型的可变数据已经被教授和学习多年了。 最后,可变状态可能是最合适的解决方案,这是三遍。 出于最后一个原因并且为了完整起见,我现在来看一下可变状态的使用如何减少方法必须期望的参数数量。

有状态的单例和静态变量

一个Java实现的辛格尔顿和其他公共Java 静态字段通常可用于同一范围内的任何Java代码的Java虚拟机 (JVM)和加载与同一 类加载器 [对于更多细节 ,请参见当是一个Singleton不是单身? ]。

通用存储的任何数据(至少从JVM / classloader角度而言)已可供同一JVM中的客户端代码使用并由相同的类加载器加载。 因此,无需在同一JVM / classloader组合中的客户端与方法或构造函数之间传递数据。

实例状态

虽然“静态”被认为是“全局可用的”,但也可以类似的方式使用较窄的实例级状态,以减少在同一类的方法之间传递参数的需求。 与全局变量相比,此方法的一个优点是可访问性仅限于类的实例( 私有字段)或类的子实例( 私有字段)。 当然,如果字段是public ,则可访问性是相当开放的,但是相同的数据不会自动提供给同一JVM / classloader中的其他代码。

下一个代码清单演示了状态数据如何以及有时用于减少给定类内部的两个方法之间对参数的需求。

用于避免传递参数的实例状态示例

/*** Simple example of using instance variable so that there is no need to* pass parameters to other methods defined in the same class.*/public void doSomethingGoodWithInstanceVariables(){this.person =Person.createInstanceWithNameAndAddressOnly(new FullName.FullNameBuilder(new Name("Flintstone"), new Name("Fred")).createFullName(),new Address.AddressBuilder(new City("Bedrock"), State.UN).createAddress());printPerson();}/*** Prints instance of Person without requiring it to be passed in because it* is an instance variable.*/public void printPerson(){out.println(this.person);}

上面的示例经过精心设计和简化,但确实说明了这一点:实例变量person可以由同一类中定义的其他实例方法访问,因此不需要在这些实例方法之间传递实例。 这确实减少了潜在的public可访问性意味着可以由外部方法使用)内部方法的签名,但是还引入了状态,现在意味着所调用的方法会影响同一对象的状态。 换句话说,不必传递参数的好处是以另一个可变状态为代价的。 为了进行比较,权衡的另一面需要传递Person实例,因为它不是实例变量,因此需要传递该实例。

传递参数而不使用实例变量的示例

/*** Simple example of passing a parameter rather than using an instance variable.*/public void doSomethingGoodWithoutInstanceVariables(){final Person person =Person.createInstanceWithNameAndAddressOnly(new FullName.FullNameBuilder(new Name("Flintstone"), new Name("Fred")).createFullName(),new Address.AddressBuilder(new City("Bedrock"), State.UN).createAddress());printPerson(person);}/*** Prints instance of Person that is passed in as a parameter.* * @param person Instance of Person to be printed.*/public void printPerson(final Person person){out.println(person);}

前两个代码清单说明可以通过使用实例状态来减少参数传递。 我通常更喜欢不要仅使用实例状态来避免参数传递。 如果出于其他原因需要实例状态,那么减少传递的参数是一个不错的附带好处,但是我不喜欢引入不必要的实例状态来简单地删除或减少参数数量。 尽管有时候在大型单线程环境中,精简参数的可读性可能已经证明了实例状态的合理性,但我认为,在越来越多的情况下,精简参数带来的轻微可读性不值得以非线程安全的类为代价多线程的世界。 我仍然不喜欢在同一个类的方法之间传递大量参数,但是我可以使用parameters对象 (也许使用package-private scope class )来减少这些参数的数量并将该参数对象传递给周围而不是大量的参数。

JavaBean样式构造

JavaBeans约定/样式已在Java开发社区中变得非常流行。 诸如Spring Framework和Hibernate之类的许多框架都依赖于遵循JavaBeans约定的类,并且某些标准(如Java Persistence API)也围绕JavaBeans约定构建。 JavaBeans风格流行的原因有很多,包括它的易用性以及针对遵循此约定的此代码使用反射的能力,以避免进行额外的配置。

JavaBean样式的基本思想是使用无参数构造函数实例化一个对象,然后通过单参数“ set”方法设置其字段,然后通过无参数“ get”方法访问其字段。 在下面的代码清单中将对此进行演示。 第一个清单显示了一个简单的PersonBean类示例,该类具有无参数构造函数以及getter和setter方法。 该代码清单还包括它使用的某些JavaBeans样式的类。 该代码清单后跟使用该JavaBean样式类的代码。

JavaBeans样式类的示例

public class PersonBean
{private FullNameBean name;private AddressBean address;private Gender gender;private EmploymentStatus employment;private HomeownerStatus homeOwnerStatus;/** No-arguments constructor. */public PersonBean() {}public FullNameBean getName(){return this.name;}public void setName(final FullNameBean newName){this.name = newName;}public AddressBean getAddress(){return this.address;}public void setAddress(final AddressBean newAddress){this.address = newAddress;}public Gender getGender(){return this.gender;}public void setGender(final Gender newGender){this.gender = newGender;}public EmploymentStatus getEmployment(){return this.employment;}public void setEmployment(final EmploymentStatus newEmployment){this.employment = newEmployment;}public HomeownerStatus getHomeOwnerStatus(){return this.homeOwnerStatus;}public void setHomeOwnerStatus(final HomeownerStatus newHomeOwnerStatus){this.homeOwnerStatus = newHomeOwnerStatus;}
}/*** Full name of a person in JavaBean style.* * @author Dustin*/
public final class FullNameBean
{private Name lastName;private Name firstName;private Name middleName;private Salutation salutation;private Suffix suffix;/** No-args constructor for JavaBean style instantiation. */private FullNameBean() {}public Name getFirstName(){return this.firstName;}public void setFirstName(final Name newFirstName){this.firstName = newFirstName;}public Name getLastName(){return this.lastName;}public void setLastName(final Name newLastName){this.lastName = newLastName;}public Name getMiddleName(){return this.middleName;}public void setMiddleName(final Name newMiddleName){this.middleName = newMiddleName;}public Salutation getSalutation(){return this.salutation;}public void setSalutation(final Salutation newSalutation){this.salutation = newSalutation;}public Suffix getSuffix(){return this.suffix;}public void setSuffix(final Suffix newSuffix){this.suffix = newSuffix;}@Overridepublic String toString(){return  this.salutation + " " + this.firstName + " " + this.middleName+ this.lastName + ", " + this.suffix;}
}package dustin.examples;/*** Representation of a United States address (JavaBeans style).* * @author Dustin*/
public final class AddressBean
{private StreetAddress streetAddress;private City city;private State state;/** No-arguments constructor for JavaBeans-style instantiation. */private AddressBean() {}public StreetAddress getStreetAddress(){return this.streetAddress;}public void setStreetAddress(final StreetAddress newStreetAddress){this.streetAddress = newStreetAddress;}public City getCity(){return this.city;}public void setCity(final City newCity){this.city = newCity;}public State getState(){return this.state;}public void setState(final State newState){this.state = newState;}@Overridepublic String toString(){return this.streetAddress + ", " + this.city + ", " + this.state;}
}

JavaBeans样式实例化和填充的示例

public PersonBean createPerson(){final PersonBean person = new PersonBean();final FullNameBean personName = new FullNameBean();personName.setFirstName(new Name("Fred"));personName.setLastName(new Name("Flintstone"));person.setName(personName);final AddressBean address = new AddressBean();address.setStreetAddress(new StreetAddress("345 Cave Stone Road"));address.setCity(new City("Bedrock"));person.setAddress(address);return person;}

刚刚显示的示例演示了如何使用JavaBeans样式方法。 这种方法做出了一些让步,以减少将大量参数传递给类的构造函数的需要。 相反,不会将任何参数传递给构造函数,并且必须设置每个需要的属性。 JavaBeans样式方法的优点之一是,与具有大量参数的构造函数相比,可读性得到了增强,因为希望每个“ set”方法都以可读的方式命名。

JavaBeans方法易于理解,在构造函数的情况下,绝对可以达到减少冗长参数的目的。 但是,这种方法也有一些缺点。 一个优点是很多繁琐的客户端代码,它们一次实例化对象并设置其属性。 忽略设置必需属性的方法很容易,因为在不离开JavaBeans约定的情况下,编译器无法强制设置所有必需参数。 也许最具破坏性,在最后一个代码清单中实例化了几个对象,这些对象从实例化到调用最终“设置”方法的时间,处于不同的不完整状态。 在这段时间内,对象处于真正的“未定义”或“不完整”状态。 “设置”方法的存在必然意味着该类的属性不能为final ,从而使整个对象高度可变。

关于Java中JavaBeans模式的普遍使用 ,一些可靠的作者对它的价值提出了质疑 。 艾伦·霍鲁布(Allen Holub)的有争议的文章为什么getter和setter方法是邪恶的,开始时没有任何保留:


尽管getter / setter方法在Java中很常见,但它们并不是面向对象(OO)的。 实际上,它们会破坏代码的可维护性。 而且,大量的getter和setter方法的存在是一个危险信号,即从OO角度来看,该程序不一定设计得很好。

乔什·布洛赫(Josh Bloch)用较不那么有力和更柔和的说服力说着JavaBeans的getter / setter风格:“ JavaBeans模式有其自身的严重缺点”( Effective Java ,第二版, 项目#2 )。 在这种情况下,Bloch推荐使用构建器模式代替对象构建。

当我出于其他原因选择的框架需要JavaBeans get / set样式并且使用该框架的理由证明了我的理由时,我不反对使用JavaBeans get / set样式。 在某些方面,JavaBeans样式类也特别适合,例如与数据存储进行交互并保存数据存储中的数据以供应用程序使用。 但是,我不喜欢使用JavaBeans样式实例化问题,只是为了避免传递参数。 为此,我更喜欢其他方法之一,例如builder。

优势与优势

我在本文中介绍了减少方法或构造函数的参数数量的不同方法,但是它们也有相同的权衡:暴露可变状态以减少或消除必须传递给方法或构造函数的参数数量。到构造函数。 这些方法的优点是简单,通常可读(尽管“全局变量”可能很难读取)以及易于首次编写和使用。 当然,从本文的角度来看,它们的最大优点是它们通常消除了传递任何参数的需要。

成本与劣势

该职位分享的所有方法的特点是易变状态的暴露。 如果在高度并发的环境中使用代码,则可能导致极高的成本。 当对象状态暴露给任何人随意修改时,存在一定程度的不可预测性。 可能很难知道哪个代码进行了错误的更改或未能进行必要的更改(例如,在填充新实例化的对象时未能调用“ set”方法)。

结论

甚至我之前介绍过的某些减少参数的方法(例如自定义类型和参数对象)都可以以(可选)可变状态的方式实现,但这些方法不需要可变状态。 相反,本文中涉及的减少方法和构造函数参数的方法确实需要可变状态。

尽管有一些缺点,但本文中介绍的某些方法还是非常受欢迎的。 这可能是出于多种原因,包括在流行框架中的普遍使用(迫使框架的用户使用该样式,还为其他人提供了自己的代码开发示例)。 这些方法之所以受欢迎的其他原因是,相对容易的初始开发以及使用这些方法进行设计时似乎(欺骗性)相对较少的思考。 总的来说,我更愿意花更多的设计和实现工作来使用构建器,而在可行时使用较少可变的方法。 但是,在某些情况下,这些可变状态方法可以很好地减少传递的参数数量,并且不会带来比已经存在的更多的风险。 我的感觉是,Java开发人员应该仔细考虑使用任何可变的Java类,并确保可变性是所希望的,或者是使用可变状态方法的原因所证明的成本。

参考: Java方法中的参数太多,第7部分: JCG合作伙伴 Dustin Marx在《 实际事件的启发》博客上的可变状态 。

翻译自: https://www.javacodegeeks.com/2013/11/too-many-parameters-in-java-methods-part-7-mutable-state.html

java 可变参数方法

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

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

相关文章

联想笔记本e480恢复出厂设置_联想e480进入bios设置_thinkpade480进入bios的方法

ThinkPad E480笔记本win10改win7如何修改BIOS设置?想要将预装win10系统换成win7系统,最重要的一步就是修改bios设置,这样才能在装机过程中不受到报错困扰,所以今天快启动小编为大家分享了详细图文教程,一起来看看吧。ThinkPad E4…

oracle一页显示15行,oracle rownum分页与显示记录小测

同事问及关于rownum表记录不显示问题,经查阅官方手册,附上测试笔记:SQL> insert into t_rownum select level from dual connect by level<5;5 rows insertedSQL> commit;Commit completeSQL> select * from t_rownum;A---------------------------------------123…

自动化用户特定实体的访问控制

实际上&#xff0c;每个Web应用程序都应该有多个用户&#xff0c;每个用户都有一些数据-帖子&#xff0c;文档&#xff0c;消息等等。 最明显的事情是保护这些实体免遭非这些资源合法所有者的用户获取。 不幸的是&#xff0c;这不是最容易的事情。 我并不是说很难&#xff0c;…

sqlserver安装显示句柄无效_Sqlserver 2016 R Service环境安装的各种错误(坑)解决办法...

相信很多朋友都会慕名Sqlserver 2016的R语言功能&#xff0c;将自己的数据库升级到Sqlserver 2016&#xff0c;但是当你安装完Sqlserver 2016的R语言组件之后&#xff0c;你会发现并不能直接使用&#xff0c;比如当你在SSMS中执行下面这段测试R语言命令的时候&#xff0c;Sqlse…

oracle用一个字段往上递归,深入sql oracle递归查询

获取数据库所有表名&#xff0c;表的所有列名 select name from sysobjects where xtypeuselect name from syscolumns where id(select max(id) from sysobjects where xtypeu and name表名)查询数据sql语句里的递归查询 sqlServer2005和Oracle 两个版本以前使用Oracle&#x…

如何在Spring Boot App中集成H2数据库

你好朋友&#xff0c; 在本教程中&#xff0c;我们将尝试探索如何在Spring Boot应用程序中与H2数据库集成。 在进行检查之前&#xff0c;让我们了解有关H2数据库的一些基础知识&#xff0c;如下所述&#xff0c;然后我们将讨论H2数据库与Spring Boot的集成。 什么是H2数据库…

python在路径里添加变量_想学Python?那就先从头开始吧!

作为人工智能和大数据时代最具竞争力的 Python 语言&#xff0c;越来越多的出现在各大编程热搜排行榜上。首先你要了解什么是python了解Python语言Python是一种解释型, 面向对象, 动态数据类型的高级程序设计语言.Python由Guido van Rossum&#xff08;荷兰&#xff09; 于1989…

php生成16位不重复随机码,PHP n个不重复的随机数生成代码

复制代码代码如下://range是将1到100列成一个数组$numbersrange(1,100);//shuffle将数组顺序随即打乱shuffle($numbers);//array_slice取该数组中的某一段$no6;$resultarray_slice($numbers,0,$no);for($i0;$iecho$result[$i]."";}print_r($result);?>复制代码代…

ant 走马灯面板指示显示不出来_触摸屏报警信息显示设置方法

人机界面(HMI)是自动化设备中非常常用的器件&#xff0c;用于替代操作面板上的实体按钮或者显示指示。人机界面的适用极大的减小了设备操作面板的尺寸&#xff0c;提升了设备的整体美观度。随着自动化设备的自动化程度的提高&#xff0c;也对设备的报警信息提出了更高的要求。如…

oracle bbed 使用,Oracle BBED使用 四步快速启动Oracle BBED

Oracle BBED使用&#xff0c;四步快速启动Oracle BBED&#xff0c;环境&#xff1a;Oracle 10g RHEL 5.8&#xff0c;介绍&#xff1a;BBED全称为数据块浏览和编辑。用于对Oracle blo环境&#xff1a;Oracle 10g RHEL 5.8介绍&#xff1a;BBED全称为数据块浏览和编辑。用于对Or…

python打包加版本信息_使用pyi-set_version为PyInstaller打包出来的程序附加版本信息...

本文将讲述如何使用 pyi-grab_version获取版本信息的模板文件&#xff0c;以及使用 pyi-set_version为打包好的程序附加版本信息。当然了&#xff0c;在开始前&#xff0c;需要你已经安装好了 PyInstaller 这个工具。如果已经安装&#xff0c;你可以在 Python 的安装目录&#…

linux读取.data文件,[20121227]v$datafile访问是从数据文件获取信息吗.txt

[20121227]v$datafile访问是从数据文件获取信息吗.txt记得以前开始学习oracle的时候,被问及访问v$datafile从哪里获取信息,当时想都没想,从数据文件.虽然当时对方并没有告诉答案,显然认为水平不再同一档次上.直到有一次看一篇blog:SELECT controlfile "SCN location"…

java 常见错误_Java常见错误的十大列表(前100名!)

java 常见错误前10名名单非常受欢迎&#xff0c;有趣且内容丰富。 但是有很多&#xff01; 如何选择合适的&#xff1f; 这是一个元前10名列表&#xff0c;可帮助您找到前10名的前10名列表。 在更令人讨厌的笔记上&#xff1a; SELECT TOP 10 mistake FROM source1 UNION ALL …

dll侧加载_win7系统开机提示xxxdll模块已加载但找不到入口点的解决方法

很多小伙伴都遇到过win7系统开机提示xxxdll模块已加载但找不到入口点的困惑吧&#xff0c;一些朋友看过网上零散的win7系统开机提示xxxdll模块已加载但找不到入口点的处理方法&#xff0c;并没有完完全全明白win7系统开机提示xxxdll模块已加载但找不到入口点是如何解决的&#…

linux md5加密文件,Linux下对字符串进行MD5加密

深入学习golang&lpar;1&rpar;—数组与切片数据(array)与切片(slice) 数组声明: ArrayType "[" ArrayLength "]" ElementType . 例如: va ...使用apt-fast 来加速你的Ubuntu 的apt使用apt-fast 来加速你的Ubuntu 的apt sudo add-apt-repository …

JDK 12:实际中的切换语句/表达式

我的上一篇文章“ 玩JDK 12的Switch表达式 ”讨论了如何使用JDK 12 Early Access Builds尝试JEP 325 switch 表达式和语句&#xff0c;并提供了一个简单的示例。 这篇文章使用相同的JDK 12 Early Access Build 10来演示switch表达式和增强的switch语句的不同功能。 我在博客文…

broker可以禁用吗 time_Win8.1系统Runtime Broker是什么进程?可以禁用吗?

我们知道电脑一开启就有很多进程了&#xff0c;其中有一个Runtime Broker是大家比较疑惑的&#xff0c;很多人会问Win8.1系统Runtime Broker是什么进程&#xff1f;可以禁用吗&#xff1f;带着这两个问题&#xff0c;小编现在就来为大家讲解一下。一、RuntimeBroker进程介绍Run…

2. linux的日志文件在哪个目录,位于/var/log目录下的20个Linux日志文件

如果你多数时间都在使用Linux系统&#xff0c;那么你有必要知道系统的日志文件位于哪里&#xff0c;以及每个日志文件是关于哪些内容的。在系统正常的时候学习理解各种日志文件的内容&#xff0c;有助于在遇到紧急情况时通过日志文件定位问题所在。/etc/rsyslog.conf文件决定了…

linux内核4.4和4.5,[图]Linux Kernel 4.5系列第4个维护版本发布

Greg Kroah-Hartman表示&#xff1a;“我今天宣布了Linux 4.5.4内核&#xff0c;所有Linux 4.5内核系列用户必须尽快升级。已经升级的4.5.y git tree能够在git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git linux-4.5.y中获取&#xff0c;也能通过访问h…

python字符串下标截取_python 用下标截取字符串的实例

python 用下标截取字符串的实例运行环境&#xff1a; win7 64位python 2.7pycharmpython 源码如下# -*- coding: utf-8 -*-str 0123456789print(str[0:3])print(str[0:-1])print(str[-1])print(str[2:5])print(str[6:-1])print(str[6:])运行效果如下&#xff1a;0120123456789…