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 可变参数方法

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

  1. java虚拟机调优_Java虚拟机中JVM参数调优及其有用的命令

    3.1参数及调优 1.-XX:-HeapDumpOnOutOfMemoryError:当首次遭遇内存溢出时Dump出此时的堆内存. 2.-XX:HeapDumpPath=./java_pid.hpro ...

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

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

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

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

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

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

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

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

  6. java 方法参数多_java 方法参数过多,怎么处理?

    本人对java的研究甚少,之前只是用一用,从未有过深度的学习,但却意外的发现了一个问题,那就是java的参数过多会出现错误,你们可能会认为,我找到的这个问题没有任何意义,对,我也是这样想的,可是,你们 ...

  7. java私有属性和私有方法_Java 9中什么是私有的?

    java私有属性和私有方法 在进行面试时,我发现大多数应聘者都不知道Java中的private修饰符真正意味着什么. 他们对此有所了解,足以应付日常编码,但还远远不够. 这不成问题. 足够了解就足够了 ...

  8. java 方法_Java 方法 | 菜鸟教程

    Java 方法 在前面几个章节中我们经常使用到 System.out.println(),那么它是什么呢? println() 是一个方法. System 是系统类. out 是标准输出对象. 这句话 ...

  9. java -jar 未响应_Java 方法性能监控和统计工具 MyPerf4J

    一个针对高并发.低延迟应用设计的高性能 Java 性能监控和统计工具. 特性 高性能: 单线程支持每秒 1000 万次 响应时间的记录,每次记录只花费 73 纳秒 无侵入: 采用 JavaAgent ...

最新文章

  1. 计算机维护常识_系统篇
  2. ADO.NET Entity Framework 简介
  3. 【5月19日】 开源论文代码分享 分割、姿势预测,目标检测
  4. JAVA 引用传递案例分析
  5. org.hibernate.annotationexception no identifier specified for entity
  6. java记录目录树_Java记录
  7. 20181123_任务(套件培训)
  8. L1-026 I Love GPLT (5 分)—团体程序设计天梯赛
  9. vue+express+mongoose项目构建
  10. 汉武帝发起的三大战役(河南、漠南、河西三大战役)
  11. Unity 球面行走
  12. 联想e470加装固态硬盘_ThinkPad E470C怎么安装固态硬盘?
  13. C++程序设计(实践)_老师有话说 S1| 如何学好计算机程序设计(c语言)
  14. java学习(类和对象)
  15. 第9周 Python计算生态概览
  16. 打开WPS或office,提示运行时错误‘424’或 运行错误‘429’,ActiveX部件不能创建对象
  17. 【vue系列-05】vue的生命周期(详解)
  18. Spring JdbcTemplate异常:EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
  19. 华工2020计算机应用基础校统考,2020年整合计算机应用基础(统考)·随堂练习2017秋华工答案名师精品资料...
  20. 参考文献编号批量设置为上标

热门文章

  1. 1、Spring简介
  2. 训练集样本不平衡问题对CNN的影响
  3. JTA 深度历险 - 原理与实现
  4. jsoup怎么获取两个标签之间的text?
  5. Java中的函数传递
  6. SpringCloud Config 分布式配置
  7. Eclipse导入他人的Maven工程报错
  8. 2020年日历电子版(打印版)_“温故知新”——2020年《故宫日历》(青少版)正式发布...
  9. db9针232接口波特率标准_理解串口通信以及232,485,422常见问题
  10. centos普通用户修改文件权限_用户管理(特殊权限、特殊属性、umask 默认权限 )