Java方法参数太多怎么办—Part 1—自定义类型
本文由 ImportNew - 王村平 翻译自 dzone。如需转载本文,请先参见文章末尾处的转载要求。
本文是这个系列的第一篇文章,介绍了采用自定义类型处理参数过多的问题。如果你也希望参与类似的系列文章翻译,可以加入我们的Android开发 和 技术翻译 小组。
我认为构造函数和方法过长的传递参数列表是一种红色警告(”red
 flag“)。在开发过程中,从逻辑的和功能的角度来看并非错误,但是通常意味着现在或者将来犯错误的可能性更高。通过阅读一系列文章,我发现一些解决参数列表过长的办法,或者至少这些办法可以减少参数个数、增强代码的可读性并降低发生错误的概率。任何解决问题的办法都具有优点和缺点。本文旨在通过使用自定义类型改进长参数方法和构造函数代码的可读性和安全性。
方法和构造函数的参数列表过长会产生一系列的障碍。大量的参数不仅使得代码看起来冗余,而且使得调用起来会很困难。同时,它又容易导致因疏忽而产生的参数移位(参数类型没变,但是因为位置改变值却改变了)。这些错误在特定情况下难以发现。幸运地是大多时候我们不必处理另一个参数过长的缺点:Java虚拟机(JVM)通过编译时报告错误(compile-time
 error)限制了方法的参数数量。
使用自定义类型一方面可以减少构造函数和方法的传参个数,另一方面又可以增强参数列表的可读性并且降低参数位置放错的可能性。自定义类型的实现方式包括Data
 Transfer Objects、JavaBeans、Value
 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 | /**   *   *   *   *   *   *   *   *   *   *   *   *   *   *   */  public     final     final     final     final     final     final     final     final     final     final     final  {     //  } | 
可以发现很容易搞混参数的顺序,然后把它们放错位置。我通常更乐意通过改变参数类型来做一些提高,以期减少参数个数。下面这些代码展示了如何使用自定义类型。
三个名字可以改为自定义类型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/** * * * */public{   private   publicfinal   {      this.name   }   public   {      return.name;   }   @Override   public   {      return.name;   }} | 
称呼和后缀也可以替换为如下两段代码所示的自定义类型:
Salutation.java
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 | package/** * * * */public{   DR,   MADAM,   MISS,   MR,   MRS,   MS,   SIR} | 
Suffix.java
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 | package/** * * * */public{   III,   IV,   JR,   SR} | 
其他的代码也可使用自定义类型。下面通过枚举的方式替换boolean型,以提高代码的可读性:
Gender.java
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 | package/** * * * */public{   FEMALE,   MALE} | 
EmploymentStatus.java
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 | package/** * * * */public{   EMPLOYED,   NOT_EMPLOYED} | 
HomeOwnerStatus.java
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 | package/** * * * */public{   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/** * * * */public{   private   publicfinal   {      this.address   }   public   {      return.address;   }   @Override   public   {      return.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/** * * * */public{   private   publicfinal   {      this.cityName   }   public   {      return.cityName;   }   @Override   public   {      return.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/** * * * */public{   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   final   final   final   final   final   final   final   final   final   final   final{   //} | 
在上面这段代码中,编译器会不允许使用先前那种很多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—自定义类型的更多相关文章
- java方法参数传递方式只有----值传递!
		在通常的说法中,方法参数的传递分为两种,值传递和引用传递,值传递是指将实际参数复制一份传递到方法中, 在方法中的改动将不会影响到实际参数本身,而引用传递则是指传递的是实际参数本身,在方法中的改动将会影 ... 
- java方法参数(超详细)
		前言 在上一篇文章中,壹哥给大家讲解了方法的定义.调用和返回值,但方法的内容还有很多,比如方法的参数是怎么回事?接下来壹哥会在这篇文章中,继续给大家讲解方法参数相关的知识,这就是我们今天要学习的内容. ... 
- java方法参数
		Java程序设计语言总是采用值调用.也就是说,方法得到的是所有参数的一个拷贝,特别是方法不能修改传递给它的任何参数变量的内容. 基本类型参数 1)X被初始化为percent值的一个拷贝: 2)X被乘以 ... 
- java 方法参数-值调用,引用调用问题
		(博客内容来自于core java卷一) 1. xx调用:程序设计语言中方法参数的传递方式: 引用调用(call by reference):表示方法接收的是调用者提供的变量地址. 值调用(call ... 
- Java方法参数的传递方式
		程序设计语言中,将参数传递给方法(或函数)有两种方法.按值传递(call by value)表示方法接受的是调用者提供的值:按引用调用(call by reference)表示方法接受的是调用者提供的 ... 
- 【转】java方法参数传递方式--按值传递、引用传递
		java的方法参数传递方式有两种,按值传递和引用传递 1.按值传递 参数类型是int,long等基本数据类型(八大基本数据类型),参数传递的过程采用值拷贝的方式 代码片段1: public class ... 
- 辨析Java方法参数中的值传递和引用传递
		小方法大门道 小瓜瓜作为一个Java初学者,今天跟我说她想通过一个Java方法,将外部变量通过参数传递到方法中去,进行逻辑处理,方法执行完毕之后,再对修改过的变量进行判断处理,代码如下所示. publ ... 
- java 方法参数的执行顺序
		java方法的参数的执行顺序是从左到右还是从右到左呢? 写出一下测试程序: 1 import java.util.*; 2 import java.io.*; 3 public class Test ... 
- Java方法参数:
		一个方法不能修改一个基本数据类型的参数 一个方法可以改变一个对象参数的状态 一个方法不能实现让对象参数引用一个新的对象 案例1: 一个方法不能修改一个基本数据类型的参数 String a = &quo ... 
- Java方法调用机制
		最近在编程时,修改方法传入对象的对象引用,并没有将修改反映到调用方法中.奇怪为什么结果没有变化,原因是遗忘了Java对象引用和内存分配机制.本文介绍3个点: ① 该问题举例说明 ② 简要阐述Java内 ... 
随机推荐
- babel-preset-env与stage-x的使用指南
			babel介绍 babel总共分为3个阶段: 解析.转换和生成 babel本身不具有任何转换功能, 如果没有plugin,那么经过babel的代码和输入的是相同的. babel插件分为两种 语法插件: ... 
- ASP.NET Core – Upload and Download Files (上传和下载文件)
			前言 以前得文章 Asp.net core 学习笔记 ( upload/download files 文件上传与下载 ), 这篇是修订版. Handle Upload File (处理上传文件) 我的 ... 
- Go 学习路线图
			基础阶段 学习内容: 掌握 Go 的基本语法,包括变量.常量.数据类型(如整数.浮点数.字符串.布尔值.数组.切片.映射等).运算符等. 理解程序的控制流,如条件语句(if-else.switch-c ... 
- LeetCode 650. 2 Keys Keyboard(只有两个键的键盘)(DP/质因数分解)
			最初在一个记事本上只有一个字符 'A'.你每次可以对这个记事本进行两种操作: Copy All (复制全部) : 你可以复制这个记事本中的所有字符(部分的复制是不允许的). Paste (粘贴) : ... 
- 全网最适合入门的面向对象编程教程:55 Python字符串与序列化-字节序列类型和可变字节字符串
			全网最适合入门的面向对象编程教程:55 Python 字符串与序列化-字节序列类型和可变字节字符串 摘要: 在 Python 中,字符编码是将字符映射为字节的过程,而字节序列(bytes)则是存储这些 ... 
- 【题目全解】ACGO排位赛#13
			ACGO排位赛#13 - 题目解析 感谢大家参加本次排位赛! T1 - 纪元流星雨 题目链接跳转:点击跳转 也没有特别大的难度,手动模拟一下就可以了. 解题步骤 先计算出这个人一生中第一次看到流星雨的 ... 
- 2021年第十一届数据技术嘉年华(DTC)资料分享
			数据技术嘉年华(DTC)是由由中国DBA联盟(ACDU)和墨天轮社区联合主办的数据技术领域的盛会,至今已成功举办11届,吸引和聚集了众多数据领域学术精英.领袖人物.技术专家.从业者和技术爱好者,于此进 ... 
- docker打包镜像,上传镜像仓库,使用rancher发布
			步骤一.首先将项目打包放在指定目录下 项目jar包名称为 micro-app.jar 步骤二.将jar包名称改为指定名称,执行命令 docker build -t micro-gateway: ... 
- 关于uniapp的兼容性的一些问题
			.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ... 
- linux 基础(3)基本文件操作
			目录的基本操作 在 linux 文件系统里,以斜杠 / 开头的路径是绝对路径,从根目录开始寻找:其他的路径则都是相对路径,从当前目录(working directory)开始寻找. 相对目录中常用的符 ... 
