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

在我的系列文章的第七篇中,有关解决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的可变状态,来自Inspired by Actual Events博客。

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

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

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

相关文章

命名空间不能直接包含字段或方法之类的成员是什么意思_Python 学习笔记之类与实例...

Python 学习笔记之类与实例一、定义1.1、定义类 (class) 封装一组相关数据,使之成为一个整体,并使用一种方法持续展示和维护。这有点像把零件组装成整车提供给用户,无须了解汽车的内部结构和工作原理,只要知道方向盘,刹…

跨平台开发框架 Lynx 初探

跨平台开发是目前开发较热门的方向,React Native 在这方面取得了很大的成功,同时 Flutter 也获得了非常多的关注。React Native 采用 Web 框架开发并使用 Native UI 进行渲染,很大程度上降低了 Native 开发的门槛并且提高迭代的效率&#xff…

linux ubuntu 关于vim得一些基本命令

1.vim显示行号 :set number 2. 快捷键 J 向下 K 往上 H 向左 L 向右 ctrlshiftT  打开新窗口 ctrlPage Down 所有vim窗口向下切换 ctrlPage Up 所有vim窗口向上切换 3. 复制vim打开的内容 yy 复制光标所在行 2yy 复制光标所在行往下的两行 ....…

ajax包含mysql吗_php 实例ajax与mysql怎么只查询出一条数据?

http://www.runoob.com/php/php...使用这个实例操作之后为什么只显示一条数据,如何让符合条件的数据全部显示出来如,我使用的查询字段是yesterday_str,查询2017-04-18这个数据怎么样才能把2017-04-18包含这个的全部数据提取出来?p…

使用Servlet 3.0,Redis / Jedis和CDI的简单CRUD –第2部分

在本文中,我们将重点介绍CDI和Servlet 3.0。 您可以在此处看到第1部分。 让我们从CDI开始。 当我开始撰写源自该系列的文章时,我并没有考虑撰写CDI。 真诚地说,我以前从未使用过。 这篇文章的想法是使用jedis和servlet创建一个对象。 但是&a…

Safari支持Service Worker,PWA还有多久爆发?

作者 | 彭星 编辑 | 尾尾 在之前的文章《PWA 将带来新一轮大前端技术洗牌?》中,我们回顾了 Web 在移动时代遭遇的两大枷锁,并就PWA是否能真正弥补 Web 劣势进行了分析,同时,提出“根据当前的发展趋势,PWA…

安装python3 及virtual与virtualenvwrapper

安装python3 下载python源码包 网址:https://www.python.org/downloads/release/python-362/ 下载地址:https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tar.xz 安装python前的库环境,非常重要 yum install gcc patch libffi-devel py…

响应式设计与前端工程性

一:响应式的几个基本知识 字体选择 有衬线和无衬线,那种字体看自己的美学意识和考虑 透视比例与体验一致性保证(人眼的位置) 行高,字体大小,间距,要根据整个展示范围的透视比例合理的去规划pc 离…

aspx写入mysql_Asp.net用户登陆数据库验证与注册写入数据库

1.思路与效果图Index.aspx注册注册成功登陆登陆验证通过进入内容页1登陆没通过验证思路:首先建一个Sqlserver数据库Student,再建一个student表(name,pwd)存放用户名和密码。然后注册功能的实现:通过数据库插入信息到表的Sql语句来实现,成功提…

在WildFly中将Apache Camel和Spring添加为jboss模块

这些天,我在玩Wildfly , Apache Camel和Spring 。 在EAR / WAR之间进行通信的一种简单方法是使用Camel的direct-vm组件。 有或没有骆驼,有很多方法可以实现这一目标。 骆驼在WildFly中就像一个饰物,不需要任何额外的配置。 骆驼很…

页面体验提升小技巧—渐进式图片

前端性能方面有许多可优化的点,而这些优化带来的就是用户体验的提升。今天我们要聊的东西并不能给性能带来提升,但却能在一定程度上提升用户的体验。 参考博客 场景:在访问页面的时候如果图片较大或者网速慢的情况我们会看到图片加载起来是有…

php mysql无限分类排序_PHP 无限级分类、排序

lyk625358header(content-type:text/html;charsetutf-8);echo "";//-无限级排序,自己优化改良的,清除上次调用此函数后留下的静态变量的值$arr array(array(id>2,cname>分类2,parent_id>1),array(id>9,cname>分类9,parent_id>8),array(id>1,cn…

Java方法中的参数太多,第5部分:方法命名

在上一篇文章 (有关处理Java方法中过多参数的系列文章的 第4部分 )中,我将方法重载视为一种向客户提供需要较少参数的方法版本或构造函数的方法。 我描述了该方法的一些缺点,并建议从方法重载中摆脱出来以使用不同名称的方法至少可…

微信小程序搭配小白接口,自己没有服务器也能开发哦

这里将重点介绍,在自己没有服务器的情况下,如何在微信小程序里直接调用小白接口。 前提 假设你已经开通微信小程序,如果还没有,可前往微信公众平台开通:https://mp.weixin.qq.com 假设你已经开通小白接口&#xff0c…

LeetCode:34. 在排序数组中查找元素的第一个和最后一个位置

1、题目描述 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。 你的算法时间复杂度必须是 O(log n) 级别。 如果数组中不存在目标值,返回 [-1, -1]。 示例 1: 输入: nums [5,7,7,8,8,10], targ…

怎么将自己做好的网站发布到互联网上呢?

如何将自己的网站上传到网站空间。 1.需要有一个上传网站的软件,在这里推荐大家使用 FTP全称是flashfxp这个软件,这个功能功能齐全而且操作简单。大家可以先去下载一下这个软 件 2. 打开FTP,界面如下 3.我们要点击链接按钮,然后FT…

vue动态生成下拉框_vue+elementui 动态创建下拉框

v-for"(domain, index) in dynamicValidateForm.domains":label"‘站点‘ index":key"domain.id">v-for"item in testData":key"item.id":label"item.testName":value"item.id":disabled"item…

[贝聊科技]网页端「应用跳转」技术实现演变

本文作者:Mr.Luo ,贝聊前端经理。本文同时发布于作者 个人博客 。 由于网页传播的便捷性,从网页向APP导流几乎是所有APP厂商都会采用的推广手段,具体来说就是在网页上提供一些触发点(例如按钮、链接)&#…

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

前10名名单非常受欢迎,有趣且内容丰富。 但是有很多! 如何选择合适的? 这是一个元前10名列表,可帮助您找到前10名的前10名列表。 在更令人讨厌的笔记上: SELECT TOP 10 mistake FROM source1 UNION ALL SELECT TOP 10…

Ubuntu 16.04 下octave的使用入门

SciLab和octave是开源的且免费的矩阵计算工具,二者都有希望成为矩阵计算的新宠。相比之下, octave与MatLab的兼容性更高。octave遵循GPL协议(GNU General Public License),用户可以单独发行octave或者包含在其产品中发…