本文由 ImportNew - 王村平 翻译自 dzone。如需转载本文,请先参见文章末尾处的转载要求。

本文是这个系列的第一篇文章,介绍了采用自定义类型处理参数过多的问题。如果你也希望参与类似的系列文章翻译,可以加入我们的Android开发 和 技术翻译 小组。

我认为构造函数和方法过长的传递参数列表是一种红色警告(”red
flag
“)。在开发过程中,从逻辑的和功能的角度来看并非错误,但是通常意味着现在或者将来犯错误的可能性更高。通过阅读一系列文章,我发现一些解决参数列表过长的办法,或者至少这些办法可以减少参数个数、增强代码的可读性并降低发生错误的概率。任何解决问题的办法都具有优点和缺点。本文旨在通过使用自定义类型改进长参数方法和构造函数代码的可读性和安全性。

方法和构造函数的参数列表过长会产生一系列的障碍。大量的参数不仅使得代码看起来冗余,而且使得调用起来会很困难。同时,它又容易导致因疏忽而产生的参数移位(参数类型没变,但是因为位置改变值却改变了)。这些错误在特定情况下难以发现。幸运地是大多时候我们不必处理另一个参数过长的缺点:Java虚拟机(JVM)通过编译时报告错误(compile-time
error
限制了方法的参数数量

使用自定义类型一方面可以减少构造函数和方法的传参个数,另一方面又可以增强参数列表的可读性并且降低参数位置放错的可能性。自定义类型的实现方式包括Data
Transfer Objects
JavaBeansValue
Objects
Reference
Objects
或者其他(在Java中经典的实现方式:枚举)自定义类型。

下面来看一个例子,该方法包含多个String和boolean类型参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
   *
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
  }

可以发现很容易搞混参数的顺序,然后把它们放错位置。我通常更乐意通过改变参数类型来做一些提高,以期减少参数个数。下面这些代码展示了如何使用自定义类型。

三个名字可以改为自定义类型Name,而不是使用String。

Name.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package

dustin.examples;
 
/**
 *
Name representation.
 *
 *
@author Dustin
 */
public

final

class

Name
{
   private

final

String name;
 
   public

Name(
final

String newName)
   {
      this.name
= newName;
   }
 
   public

String getName()
   {
      return

this
.name;
   }
 
   @Override
   public

String toString()
   {
      return

this
.name;
   }
}

称呼和后缀也可以替换为如下两段代码所示的自定义类型:

Salutation.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package

dustin.examples;
 
/**
 *
Salutations for individuals' names.
 *
 *
@author Dustin
 */
public

enum

Salutation
{
   DR,
   MADAM,
   MISS,
   MR,
   MRS,
   MS,
   SIR
}

Suffix.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package

dustin.examples;
 
/**
 *
Suffix representation.
 *
 *
@author Dustin
 */
public

enum

Suffix
{
   III,
   IV,
   JR,
   SR
}

其他的代码也可使用自定义类型。下面通过枚举的方式替换boolean型,以提高代码的可读性:

Gender.java

1
2
3
4
5
6
7
8
9
10
11
12
package

dustin.examples;
 
/**
 *
Gender representation.
 *
 *
@author Dustin
 */
public

enum

Gender
{
   FEMALE,
   MALE
}

EmploymentStatus.java

1
2
3
4
5
6
7
8
9
10
11
12
package

dustin.examples;
 
/**
 *
Representation of employment status.
 *
 *
@author Dustin
 */
public

enum

EmploymentStatus
{
   EMPLOYED,
   NOT_EMPLOYED
}

HomeOwnerStatus.java

1
2
3
4
5
6
7
8
9
10
11
12
package

dustin.examples;
 
/**
 *
Representation of homeowner status.
 *
 *
@author Dustin
 */
public

enum

HomeownerStatus
{
   HOME_OWNER,
   RENTER
}

此人的地址信息可以通过如下代码所示的自定义类型作为参数进行传递。

StreetAddress.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package

dustin.examples;
 
/**
 *
Street Address representation.
 *
 *
@author Dustin
 */
public

final

class

StreetAddress
{
   private

final

String

address;
 
   public

StreetAddress(
final

String

newStreetAddress)
   {
      this.address
= newStreetAddress;
   }
 
   public

String

getAddress()
   {
      return

this
.address;
   }
 
   @Override
   public

String

toString()
   {
      return

this
.address;
   }
}

City.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package

dustin.examples;
 
/**
 *
City representation.
 *
 *
@author Dustin
 */
public

final

class

City
{
   private

final

String cityName;
 
   public

City(
final

String newCityName)
   {
      this.cityName
= newCityName;
   }
 
   public

String getCityName()
   {
      return

this
.cityName;
   }
 
   @Override
   public

String toString()
   {
      return

this
.cityName;
   }
}

State.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package

dustin.examples;
 
/**
 *
Simple representation of a state in the United States.
 *
 *
@author Dustin
 */
public

enum

State
{
   AK,
   AL,
   AR,
   AZ,
   CA,
   CO,
   CT,
   DE,
   FL,
   GA,
   HI,
   IA,
   ID,
   IL,
   IN,
   KS,
   KY,
   LA,
   MA,
   MD,
   ME,
   MI,
   MN,
   MO,
   MS,
   MT,
   NC,
   ND,
   NE,
   NH,
   NJ,
   NM,
   NV,
   NY,
   OH,
   OK,
   OR,
   PA,
   RI,
   SC,
   SD,
   TN,
   TX,
   UT,
   VA,
   VT,
   WA,
   WI,
   WV,
   WY
}

通过使用自定义类型,开始展示的方法的代码变得更加易读易懂,同时更不容易出错了。下面是使用自定义类型改写后的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public

Person createPerson(
   final

Name lastName,
   final

Name firstName,
   final

Name middleName,
   final

Salutation salutation,
   final

Suffix suffix,
   final

StreetAddress address,
   final

City city,
   final

State state,
   final

Gender gender,
   final

EmploymentStatus employment,
   final

HomeownerStatus homeowner)
{
   //
implementation goes here
}

在上面这段代码中,编译器会不允许使用先前那种很多String、boolean参数的方式。这样来减少了放错参数顺序的可能性,从而帮助到开发者。但是三个名字仍然存在一个潜在的问题,因为代码的调用者依然容易搞乱它们的顺序。出于这种担心,需要为此专门定义FirstName、LastName、MiddleName 三种类型。但通常我喜欢使用一个自定义类型,里面放置上述三个名字作为新类的属性。当然那属于后来即将讲解的解决Java参数过长问题的文章的内容了。

使用自定义类型的好处和优点

提高了代码的可读性,为代码的维护者和API调用者提供了便利。提高了在编写代码时开发环境(IDE)参数匹配的能力,没有什么比使用自定义类型的编译环境检查更能帮助开发环境的了。通常来说,我更喜欢尽可能地把这些自动检查由运行环境移到编译环境,并且把他们定义为可直接调用的静态类型而非普通类型。

进一步说,自定义类型的存在也使我们将来为它添加更多细节变得更加容易。例如:将来我也许会为方法创建人添加一个全名或者把其他的状态放在枚举器当中,同时这样也不会改变自定义类型的原有面貌。这些都是使用String类型无法完成的。这样做的另一个潜在优点就是使用自定义类型拥有更大的灵活性,可以在不改变原有类型面貌的情况下改变实现方式。这些自定义类型(不包括枚举器)能够被扩展(String则不具备),并且可以在不改变它的类型的情况下灵活添加自定义细节。

自定义类型的代价和缺点

普遍存在缺点之一,就是开始需要额外的实例化和占用内存。例如:Name类需要实例化,而且封装了String。我认为之所以有人会持有这种观点,更多的是因为他从一种尚不够成熟的所谓的最优化角度,而非实用合理的角度来衡量性能问题。当然也有这种情况存在,即:额外实例化这些类型花费了太多的代价并且不能证明增强可读性和编译能力所带来的好处。然而大多时候这种额外的开销都是可以承受的,不会产生什么可见的坏影响。如果大多数时候使用自定义类型而不用String或者boolean会产生性能问题,这真的让人难以置信。

另一些人认为使用自定义类型而非内置类型需要付出额外的写和测试代码的努力。然而,正如我的这篇文章的代码所显示的那样,这些非常简单的类和枚举器写和测试起来并不难。使用一个优秀的开发环境和一门灵活的编程语言(如:Groovy),编写和测试会更加容易而且通常可以自动完成。

结论

我喜欢使用自定义类型来提高代码的可读性,将更多的编译检查负担转给编译器。我不喜欢这种传参方式的最大原因在于:这种方法本身只是提高了拥有过长参数列表的构造函数和方法的可读性却并没有减少实际需要传递的参数数量,代码的调用者依然需要写那些笨拙的客户端代码来调用构造函数和方法。因此,我通常使用其它技术而不是增加自定义类型来解决向方法传递参数过长的问题。这些技术将在接下来的文章里讲述。

原文链接: dzone 翻译: ImportNew.com王村平

译文链接: http://www.importnew.com/6518.html

[ 转载请保留原文出处、译者和译文链接。]

Java方法参数太多怎么办—Part 1—自定义类型的更多相关文章

  1. java方法参数传递方式只有----值传递!

    在通常的说法中,方法参数的传递分为两种,值传递和引用传递,值传递是指将实际参数复制一份传递到方法中, 在方法中的改动将不会影响到实际参数本身,而引用传递则是指传递的是实际参数本身,在方法中的改动将会影 ...

  2. java方法参数(超详细)

    前言 在上一篇文章中,壹哥给大家讲解了方法的定义.调用和返回值,但方法的内容还有很多,比如方法的参数是怎么回事?接下来壹哥会在这篇文章中,继续给大家讲解方法参数相关的知识,这就是我们今天要学习的内容. ...

  3. java方法参数

    Java程序设计语言总是采用值调用.也就是说,方法得到的是所有参数的一个拷贝,特别是方法不能修改传递给它的任何参数变量的内容. 基本类型参数 1)X被初始化为percent值的一个拷贝: 2)X被乘以 ...

  4. java 方法参数-值调用,引用调用问题

    (博客内容来自于core java卷一) 1. xx调用:程序设计语言中方法参数的传递方式: 引用调用(call by reference):表示方法接收的是调用者提供的变量地址. 值调用(call ...

  5. Java方法参数的传递方式

    程序设计语言中,将参数传递给方法(或函数)有两种方法.按值传递(call by value)表示方法接受的是调用者提供的值:按引用调用(call by reference)表示方法接受的是调用者提供的 ...

  6. 【转】java方法参数传递方式--按值传递、引用传递

    java的方法参数传递方式有两种,按值传递和引用传递 1.按值传递 参数类型是int,long等基本数据类型(八大基本数据类型),参数传递的过程采用值拷贝的方式 代码片段1: public class ...

  7. 辨析Java方法参数中的值传递和引用传递

    小方法大门道 小瓜瓜作为一个Java初学者,今天跟我说她想通过一个Java方法,将外部变量通过参数传递到方法中去,进行逻辑处理,方法执行完毕之后,再对修改过的变量进行判断处理,代码如下所示. publ ...

  8. java 方法参数的执行顺序

    java方法的参数的执行顺序是从左到右还是从右到左呢? 写出一下测试程序: 1 import java.util.*; 2 import java.io.*; 3 public class Test ...

  9. Java方法参数:

    一个方法不能修改一个基本数据类型的参数 一个方法可以改变一个对象参数的状态 一个方法不能实现让对象参数引用一个新的对象 案例1: 一个方法不能修改一个基本数据类型的参数 String a = &quo ...

  10. Java方法调用机制

    最近在编程时,修改方法传入对象的对象引用,并没有将修改反映到调用方法中.奇怪为什么结果没有变化,原因是遗忘了Java对象引用和内存分配机制.本文介绍3个点: ① 该问题举例说明 ② 简要阐述Java内 ...

随机推荐

  1. 基于GitLab+Jenkin-CICD方案实践

    前言 笔录于2022- 官网:https://about.gitlab.com/ 参考文档:https://docs.gitlab.com/ee/ci/ 清华源:清华大学开源软件镜像站 | Tsing ...

  2. effective-python-14

    effective python-Item 14 list.sort函数,比较容易用的是reverse,而其中还有个参数key,默认是NONE,此时排序是按照默认比较方法排序,比如数据按照大小,字符串 ...

  3. Angular 18+ 高级教程 – Component 组件 の Dynamic Component 动态组件

    前言 Angular 是 MVVM 框架. MVVM 的宗旨是 "不要直接操作 DOM". 为了这个 "不要直接操作 DOM",我们已经讲了 2 篇文章了. 第 ...

  4. CSS & JS Effect – Styling Input Radio

    原生 Radio 的 Limitation <input type="radio" style="width: 25px; height: 25px; cursor ...

  5. Angular 学习笔记 (Angular 12 get started)

    Angular 12 视乎比以往更稳定了. 这里记入一般的 get started 结构和做法. 第 1 步, 创建项目. ng new project --create-application=fa ...

  6. vivo 全链路多版本开发测试环境落地实践

    作者:来自 vivo 互联网研发效能团队- Wang Kang 测试环境全链路多版本部署,解决多测试环境资源争抢等问题. 一.背景介绍 软件系统中全链路指的是从用户请求发起,到最终返回响应的整个过程中 ...

  7. Windows 缺失Qt5.xxxx.dll,无法继续执行代码

    事件起因: 客户自行安装完Autodesk系软件后, 软件一直弹窗报错 AutodDesktopApp.exe - 系统错误 Windows软件报错:由于找不到Qt5.xxxx.dll,无法继续执行代 ...

  8. Python技术书示例代码相关内容

    二维码用微信扫,按提示填写你的邮箱,转到电脑上打开邮箱下载.清华国企网盘,比较快速.安全.放心下载. 百度网盘链接 用Edge/Chrome浏览器下载. <Python自动化运维快速入门> ...

  9. 浏览器中生成 OSS 令牌 | Web Crypto API

    笔者写文章的时候,都会把图片通过自己搭建的一个简单站点 https://imgbed.sugarat.top/ 把图片上传到各种云的对象存储服务(OSS)上. 然后通过CDN访问,保证图片有可靠的访问 ...

  10. “技术沙龙”来袭,邀您一同探讨 Serverless 数据库技术最佳实践

    如今,随着数据库的上云趋势,企业用户对业务连续性的要求越来越高,基于Serverless架构下的数据库也应运而生. Serverless数据库技术可以满足客户在公有云计算环境下根据业务发展弹性扩展集群 ...