概述
在前一篇文章中,我关注了一些关于构造函数或方法参数过多的问题。文中我讨论了用自定义类型代替基本、内置类型以获得良好的可读性和安全性。然而这并不能减少参数的数量。这次,我将用参数对象方法给构造函数和方法的参数“瘦身”。
通常你会看到一组特定参数,它们关系紧密并且总是一起传给方法或构造函数,有可能好几个函数都使用这一组参数。这些函数可能属于同一个类,也可能属于不同的类。
这时,《重构》这本书中介绍的“引入参数对象”的方法能很好地解决问题。该方法可以描述为运用一个对象封装这些参数,再以该对象取代他们。本文就是要演示这一“重构”。
为演示好“引入参数对象”这一重构方法,我们首先看下上篇文章曾使用过的代码示例。
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
}
|
前面说过,这种传统方法对调用者来说非常乏味。不但因为没有类型安全保证导致传参时容易搞混,而且代码可读性也不是很理想。幸运的是,可以通过“引入参数对象”方法重构示例代码。“names”型参数能合并写进一个“FullName”类,同理”address”型参数也一样。其他剩余参数因为相互联系不那么紧密没法合并到一个新的类中。
在引入参数对象重构后参数数量减少了,方法调用相比以前也变得更加简单。这将在下面的代码中得以体现。
1
2
3
4
5
6
7
8
9
|
public
Person createPerson(
final
FullName fullName,
final
Address address,
final
boolean
isFemale,
final
boolean
isEmployed,
final
boolean
isHomeOwner)
{
return
new
Person();
}
|
上面的示例中只有5个参数,这意味着可读性更强和调用也更方便。从类型角度看也更为安全,因为不会将名字和地址类型的字符串混淆。遗憾的是,三个布尔型参数依然存在这种隐患。下面代码展示了FullName 和Address类:
FullName.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
61
|
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;
}
@Override
public
String toString()
{
return
this
.salutation +
" "
+
this
.firstName +
" "
+
this
.middleName
+
this
.lastName +
", "
+
this
.suffix;
}
}
|
Address.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
|
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;
}
@Override
public
String toString()
{
return
this
.streetAddress +
", "
+
this
.city +
", "
+
this
.state;
}
}
|
尽管代码得到了改进,仍然有一些问题有待结局。尤其是原来的方法参数包含了3个布尔类型,彼此之间很容易混淆。尽管字符串类型参数被重构到了两个新类,但是这两个类中还是包含了很多的字符串。这种情况就需要为引入的参数对象用自定义类型重构。使用上篇中展示的自定义类型,重构以后的参数如下面代码所示:
1
2
3
4
5
6
7
8
9
|
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 (自定义类型)
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
61
|
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;
}
@Override
public
String toString()
{
return
this
.salutation +
" "
+
this
.firstName +
" "
+
this
.middleName
+
this
.lastName +
", "
+
this
.suffix;
}
}
|
Address.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
|
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;
}
@Override
public
String toString()
{
return
this
.streetAddress +
", "
+
this
.city +
", "
+
this
.state;
}
}
|
现在,所有的示例都使用了独立的公共类。如果参数对象类位于同一个包的作用域中,就能将参数对象用于方法和构造函数之间传递信息,即使只能达成这一点也是非常有用的。另外在一些情况下,嵌套类也能当参数对象使用。
引入参数对象的好处与优点
最明显好处在于参数对象减少了构造函数或方法的传参数量。相关的参数一起封装更容易确定传递给构造函数和方法的是什么类型的参数。参数数量减少带给开发者的好处是显而易见的。
参数对象跟上篇文章讨论过的自定义类型有一个相同的好处:可以方便地为参数对象添加额外的行为和特征。 如用一个Address类来验证地址信息,而不是使用一堆字符串。
引入参数对象的代价与缺点
参数对象的主要缺点是需要额外的工作来设计、实现和测试相关类。但是,如果我们借助IDE和脚本语言,其中大量繁琐的步骤都能自动完成。当然,关于参数对象还有一个更小的争论:参数对象可能被滥用。如果一个开发者纯粹为了减少参数数量,把联系不紧的几个参数强捆在一个类中这肯定是行不通的,在可读性上甚至适得其反。
总结
参数对象通过恰当封装联系紧密的参数来减少方法和构造函数的参数数量。它们易于实现,能显著提高方法和构造函数在参数传递时的可读性和安全性。正如我上篇文章所说,参数对象如果结合自定义类型等一起使用效果会更好。
最后
以上就是唠叨小鸭子为你收集整理的Java方法参数太多怎么办—Part 2—引入参数对象的全部内容,希望文章能够帮你解决Java方法参数太多怎么办—Part 2—引入参数对象所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复