本文由 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. LeetCode题集-2 - 两数相加

    这个题目是什么意思呢?简单来说就是把两个链表平铺开,头节点对齐,然后从头开始相同的节点相加,满10则进位,进位值与下个节点继续相加,当一个链表没有节点时候则可以把没有节点当做0继续与有节点的链表继续相 ...

  2. CIIS 2023 丨聚焦文档图像处理前沿领域,合合信息 AI 助力图像处理与内容安全保障

    近日,2023第十二届中国智能产业高峰论坛(CIIS 2023)在江西南昌顺利举行.大会由中国人工智能学会.江西省科学技术厅.南昌市人民政府主办,南昌市科学技术局.中国工程科技发展战略江西研究院承办. ...

  3. RxJS 系列 – Filtering Operators

    前言 介绍完 RxJS 两大概念 Observable 和 Subject 之后, 篇幅最大的就是各种 Operators 了. 这篇先介绍比较简单的 Filter Operators. 请先看完上面 ...

  4. JavaScript – 类型转换

    介绍 JS 是弱类型语言, 在编程时, 有许多自动类型转换的技巧, 虽然大家都不太鼓励, 尤其是用了 TypeScript 之后, 但无可否认自动转换很方便, 看上去也很干净. 所以这篇还是要介绍一些 ...

  5. Google Analytics & Ads 学习笔记

    更新: 2021-09-13 Naming conversion for event category, action, label https://support.google.com/analyt ...

  6. JAVAEE——idea安装

    一.安装idea 1.点击这里下载jar包 2.点击这里下载idea2019    提取码:1234 (也可自行官网下载付费版自行寻找破解) 3.下载后jar压缩包后解压,把里面那个jar文件随意放在 ...

  7. Qt表格入门

    摘要     表格作为数据展示的界面,会在很多场景下使用.Qt为我们提供了使用简单方便和扩展性强的表格视图,这里做一个简单的入门整理.     个人能力有限,有错误欢迎留言指正,如果你有更好的方法,也 ...

  8. Project: Kill e

    接到上级任务,今天来暗杀 \(e\) 据说杀死 \(e\) 的方式就是把他算出来,好吧,现在我们还是来算一下 考虑使用如下代数式求解 \[e\ \text{site:baidu.com} \] 虽然我 ...

  9. docker安装及基本的镜像拉取

    docker 使用存储库安装 卸载它们以及相关的依赖项. yum remove docker \ docker-client \ docker-client-latest \ docker-commo ...

  10. Linux 防火墙与安全管理工具详解

    Linux 防火墙与安全管理工具详解 1. Iptables 概述 Iptables 是 Linux 系统中用于控制网络流量的工具,通过定义规则来过滤.转发和修改数据包.其规则可以细致地管理进入和离开 ...