引入参数对象
某些参数总是很自然地同时出现。
动机
特定的一组参数总是被一起传递,可能是属于同一个class,也可能是不同的class。我们可以运用一个对象包装所有这些数据,就是是为了将这些数据组织在一起,也值得这么做。
新对象定义的访问函数可以使函数更容易理解。
发现一些可被移植至新建class的行为。
将共通行为移到新对象,可以减少很多重复代码。
做法
- 新建一个class,用以表现你想替换的一组参数。 将这个设为不可变的( 不可被修改的)
- 编译
- 针对使用该组参数的所有函数, 实施Add Parameter, 以上述新建class 实体对象作为新添参数, 并将此一参数值设为null
- 你可以保留修改前的旧函数, 并令它调用修改后的新函数。 然后逐一令调用端转而调用新函数, 最后再将旧函数删除
- 对于Data Clump( 数据泥团) 中的每一项( 在此均为参数) , 从函数签名式( signature) 中移除之, 并修改调用端和函数本体, 令它们都改而通过「 新建 的参数对象」 取得该值。
- 每去除一个参数, 编译并测试。
- 将原先的参数全部去除之后, 观察有无适当函数可以运用Move Method 搬移到参数对象之中。
- 是函数中的一个段落, 首先使用Extract Method 将该段落提炼为一个独立函数, 再搬移这一新建函数
范例
// Entry 实体函数
class Entry...
private Date _chargeDate;
private double _value;
Entry (double value, Date chargeDate) {
_value = value;
_chargeDate = chargeDate;
}
Date getDate(){
return _chargeDate;
}
double getValue(){
return _value;
}
=============================>
// Account 保存了一组Entry 对象, 并有一个函数用来计算两日期间的帐项总量
class Account...
double getFlowBetween (Date start, Date end) {
double result = 0;
Enumeration e = _entries.elements();
while (e.hasMoreElements()) {
Entry each = (Entry) e.nextElement();
// 把这段判断移到DateRange中去
if (each.getDate().equals(start) ||
each.getDate().equals(end) ||
(each.getDate().after(start) && each.getDate().before(end))){
result += each.getValue();
}
}
return result;
}
private Vector _entries = new Vector();
// 不过, 自从我得知Range 模式[Fowler, AP]之后, 我就尽量以「范围对象」 取而代之。
client code...
double flow = anAccount.getFlowBetween(startDate, endDate);
======================================================>
class DateRange {
// 把DateRange class 设为不可变, 也就是说, 其中所有值域都是final
private final Date _start;
private final Date _end;
// 只能由构造函数来赋值, 因此没有任何函数可以改其中任何值域值
DateRange (Date start, Date end) {
_start = start;
_end = end;
}
Date getStart() {
return _start;
}
Date getEnd() {
return _end;
}
}
========================================>
class Account...
double getFlowBetween (DateRange range) {
double result = 0;
Enumeration e = _entries.elements();
while (e.hasMoreElements()) {
Entry each = (Entry) e.nextElement();
if (range.includes(each.getDate())) {
result += each.getValue();
}
}
return result;
}
class DateRange...
...
boolean includes (Date arg) {
return (arg.equals(_start) ||
arg.equals(_end) ||
(arg.after(_start) && arg.before(_end)));
}
...