在上一篇文章中 ,我研究了与方法和构造函数的长参数列表相关的一些问题。 在那篇文章中,我讨论了用自定义类型替换基元和内置类型以提高可读性和类型安全性。 这种方法使方法或构造函数的众多参数更具可读性,但并没有减少参数的数量。 在本文中,我将研究如何使用Parameter Object来减少方法或构造函数的参数数量。
通常,使“垃圾箱”对象耦合不相关的参数通常不是一个好主意,这些参数之间的唯一关系是它们需要传递给相同的方法或构造函数。 但是,当将相关参数作为高度内聚的对象的一部分传递给构造函数或方法时,称为Introduce Parameter Object的重构是一个不错的解决方案。 重构的用法被描述为 “自然组合在一起的参数组”。 我将在这篇文章中演示这种重构。
为了演示Introduce Parameter Object重构的效用,让我们首先来看最后一篇文章中的示例,该示例在方法调用中使用了许多String
和boolean
参数。
/*** Instantiate a Person object.* * @param lastName* @param firstName* @param middleName* @param salutation* @param suffix* @param streetAddress* @param city* @param state* @param isFemale* @param isEmployed* @param isHomeOwner* @return */public Person createPerson(final String lastName,final String firstName,final String middleName,final String salutation,final String suffix,final String streetAddress,final String city,final String state,final boolean isFemale,final boolean isEmployed,final boolean isHomeOwner){// implementation goes here}
正如我在上一篇文章中所讨论的那样,这种方法对于调用者来说是乏味的,使得以错误的顺序传递参数变得非常容易,而类型安全性却很少,并且会降低代码的可读性。 幸运的是,此示例中的参数为应用Introduce Parameter Object重构提供了一些很好的机会。 “名称”参数(包括称呼和后缀)可以包含在单个全名类中。 地址参数(街道地址,城市和州)可以位于单个地址对象中。 其他参数可能不太容易分组为具有高内聚力的单个类。
使用“引入参数对象”重构的建议应用程序,由于减少了参数数量,因此前面显示的方法调用更加简单。 这显示在下一个代码清单中。
public Person createPerson(final FullName fullName,final Address address,final boolean isFemale,final boolean isEmployed,final boolean isHomeOwner){return new Person();}
上面的示例现在只有五个参数,并且更易于阅读,并且更易于客户端使用。 从键入的角度来看,它也是更安全的,因为在这种情况下几乎不可能将名称字符串与地址字符串混淆。 不幸的是,这三个布尔参数仍然有点造成潜在的混乱和云可读性。 接下来的代码清单显示FullName
和Address
类的潜在实现。
FullName.java(简单)
package dustin.examples;/*** Full name of a person.* * @author Dustin*/
public final class FullName
{private final String lastName;private final String firstName;private final String middleName;private final String salutation;private final String suffix;public FullName(final String newLastName,final String newFirstName,final String newMiddleName,final String newSalutation,final String newSuffix){this.lastName = newLastName;this.firstName = newFirstName;this.middleName = newMiddleName;this.salutation = newSalutation;this.suffix = newSuffix;}public String getLastName(){return this.lastName;}public String getFirstName(){return this.firstName;}public String getMiddleName(){return this.middleName;}public String getSalutation(){return this.salutation;}public String getSuffix(){return this.suffix;}@Overridepublic String toString(){return this.salutation + " " + this.firstName + " " + this.middleName+ this.lastName + ", " + this.suffix;}
}
Address.java(简单)
package dustin.examples;/*** Representation of a United States address.* * @author Dustin*/
public final class Address
{private final String streetAddress;private final String city;private final String state;public Address(final String newStreetAddress, final String newCity, final String newState){this.streetAddress = newStreetAddress;this.city = newCity;this.state = newState;}public String getStreetAddress(){return this.streetAddress;}public String getCity(){return this.city;}public String getState(){return this.state;}@Overridepublic String toString(){return this.streetAddress + ", " + this.city + ", " + this.state;}
}
尽管代码得到了改进,但是仍有一些问题可以改进。 特别是,具有太多参数的原始方法仍然具有三个boolean
参数,可以很容易地将它们相互混淆。 尽管该方法的String
参数被分解为两个新类,但这两个新类仍分别由一堆String
组成。 在这些情况下,可能需要使用自定义类型来补充“ 介绍参数对象”重构。 使用我在上一篇文章中显示的自定义类型,带有太多参数的方法现在看起来像下一个代码清单中所示。
public Person createPerson(final FullName fullName,final Address address,final Gender gender,final EmploymentStatus employment,final HomeownerStatus homeownerStatus){// implementation goes here}
该方法现在具有较少的参数,并且确实具有不同类型的参数。 现在,IDE和Java编译器对于确保客户端正确使用此接口特别有用。 将自定义类型(写在最后一篇文章中)应用于FullName
和Address
类会导致这些类的下两个新代码清单。
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;public 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;}
}
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;public 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
类。 我经常发现,如果我只需要一个参数对象来在同一包中的方法和构造函数之间传递信息,那么使这些参数对象类的package
作用域很有用。 在某些情况下,嵌套类也可以用于这些参数对象。
优势与优势
参数对象最明显的好处是减少了传递给方法或构造函数的参数数量。 相关参数的这种封装使快速确定要传递给方法或构造函数的类型变得更加容易。 开发人员更容易理解较少的参数。
参数对象具有与自定义类型相同的优点之一:可以为方便功能向参数对象添加其他行为和特征。 例如,拥有一个Address
类而不是一堆String
类型可以使人们验证地址。
成本与劣势
参数对象的主要缺点是设计,实现和测试类需要一些额外的工作。 但是,这些工具非常易于编写和测试,而现代工具(如IDE和脚本语言)使这些任务中最平凡和繁琐的部分的自动化更加容易。 反对这种方法的一个更小的论点是它可以被滥用。 如果开发人员开始将不相关的参数捆绑到一个类中只是为了减少参数的数量,那并不一定会解决这种情况。 这种方法的确确实减少了参数的数量,但是没有实现提高可读性的最终目标,并且可以说这种方法的可读性更低。
结论
参数对象提供了一种很好的干净方法,可以适当地封装相关参数,以减少方法或构造函数的总参数数量。 它们易于实现,并且可以显着增强传递给方法和构造函数调用的可读性和类型安全性参数。 可以通过使用自定义类型进一步增强参数对象,如我之前的文章中所述。
翻译自: https://www.javacodegeeks.com/2013/10/too-many-parameters-in-java-methods-part-2-parameters-object.html