引入参数对象
2011-12-23 12:46
218 查看
这个方法其实就是说:如果某些(2个或者两个以上)在2个或者多个函数里同时出现,这就是数据泥团,应该包装一下。
你常会看到特定的一组参数总是一起被传递。可能有好几个函数都使用这一组参数,这些函数可能隶属同一个class,也可能隶属不同的classes 。这样一组参数就是所谓的Date Clump (数据泥团)」,我们可以运用一个对象包装所有这些数据,再以该对象取代它们。哪怕只是为了把这些数据组织在一起,这样做也是值得的。本项重构的价值在于「缩短了参数列的长度」,而你知道,过长的参数列总是难以理解的。此外,新对象所定义的访问函数(accessors)还可以使代码更具一致性,这又进一步降低了代码的理解难度和修改难度。
作法(Mechanics)
例子:原来的代码:
可以看到上面的代码用一对值表示一个范围,例如表示日期范围的start 和end、表示数值范围的upper 和lower 等等;要尽量用范围对象取代,这样的话,或许还可以抽取出来逻辑函数。
第一步:首先声明一个简单的数据容器,用来表示范围:并且设置为不可变,里面所有的值域都是final,只能用构造函数来赋值,因此没有任何函数可以修改其中任何值域和值。
第二步:把新的 对象加入到方法的参数列表里:
客户端相应修改,对应的参数暂时设置为null:
第三步:删除数据参数
第四步:提取函数,移动到相关性高的类里面去。
比较实用的一个方法。
你常会看到特定的一组参数总是一起被传递。可能有好几个函数都使用这一组参数,这些函数可能隶属同一个class,也可能隶属不同的classes 。这样一组参数就是所谓的Date Clump (数据泥团)」,我们可以运用一个对象包装所有这些数据,再以该对象取代它们。哪怕只是为了把这些数据组织在一起,这样做也是值得的。本项重构的价值在于「缩短了参数列的长度」,而你知道,过长的参数列总是难以理解的。此外,新对象所定义的访问函数(accessors)还可以使代码更具一致性,这又进一步降低了代码的理解难度和修改难度。
作法(Mechanics)
· | 新建一个class,用以表现你想替换的一组参数。将这个设为不可变的(不可被修改的,immutable)。 |
· | 编译。 |
· | 针对使用该组参数的所有函数,实施Add Parameter,以上述新建class 之实体对象作为新添参数,并将此一参数值设为null 。 |
Ø | 如果你所修改的函数被其他很多函数调用,那么你可以保留修改前的旧函数,并令它调用修改后的新函数。你可以先对旧函数进行重构, 然后逐一令调用端转而调用新函数,最后再将旧函数删除。 |
· | 对于Data Clump(数据泥团)中的每一项(在此均为参数),从函数签名式(signature)中移除之,并修改调用端和函数本体,令它们都改而通过「新建 的参数对象」取得该值。 |
· | 每去除一个参数,编译并测试。 |
· | 将原先的参数全部去除之后,观察有无适当函数可以运用Move Method 搬移到参数对象之中。 |
Ø | 被搬移的可能是整个函数,也可能是函数中的一个段落。如果是后者, 首先使用Extract Method 将该段落提炼为一个独立函数,再搬移这一新建函数。 |
下面是一个「帐目和帐项」(account and entries)范例。表示「帐项」的Entry 实际上只是个简单的数据容器: class Entry... Entry (double value, Date chargeDate) { _value = value; _chargeDate = chargeDate; } Date getDate(){ return _chargeDate; } double getValue(){ return _value; } private Date _chargeDate; private double _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(); 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(); client code... double flow = anAccount.getFlowBetween(startDate, endDate);
可以看到上面的代码用一对值表示一个范围,例如表示日期范围的start 和end、表示数值范围的upper 和lower 等等;要尽量用范围对象取代,这样的话,或许还可以抽取出来逻辑函数。
第一步:首先声明一个简单的数据容器,用来表示范围:并且设置为不可变,里面所有的值域都是final,只能用构造函数来赋值,因此没有任何函数可以修改其中任何值域和值。
class DateRange { DateRange (Date start, Date end) { _start = start; _end = end; } Date getStart() { return _start; } Date getEnd() { return _end; } private final Date _start; private final Date _end; }
第二步:把新的 对象加入到方法的参数列表里:
class Account... double getFlowBetween (Date start, Date end, DateRange range) { double result = 0; Enumeration e = _entries.elements(); while (e.hasMoreElements()) { Entry each = (Entry) e.nextElement(); if (each.getDate().equals(start) || each.getDate().equals(end) || (each.getDate().after(start) && each.getDate().before(end))) { result += each.getValue(); } } return result; }
客户端相应修改,对应的参数暂时设置为null:
client code... double flow = anAccount.getFlowBetween(startDate, endDate, null);
第三步:删除数据参数
下一个步骤是去除旧参数之一,以新建对象取而代之。首先我删除start 参数,并修改getFlowBetween() 函数及其调用者,让它们转而使用新对象: class Account... double getFlowBetween (Date end, DateRange range) { double result = 0; Enumeration e = _entries.elements(); while (e.hasMoreElements()) { Entry each = (Entry) e.nextElement(); if (each.getDate().equals(range.getStart()) || each.getDate().equals(end) || (each.getDate().after(range.getStart()) && each.getDate().before(end))) { result += each.getValue(); } } return result; } client code... double flow = anAccount.getFlowBetween(endDate, new DateRange (startDate, null)); 然后我将参数也移除: class Account... double getFlowBetween (DateRange range) { double result = 0; Enumeration e = _entries.elements(); while (e.hasMoreElements()) { Entry each = (Entry) e.nextElement(); if (each.getDate().equals(range.getStart()) || each.getDate().equals(range.getEnd()) || (each.getDate().after(range.getStart()) && each.getDate().before(range.getEnd()))) { result += each.getValue(); } } return result; } client code... double flow = anAccount.getFlowBetween(new DateRange (startDate, endDate));
第四步:提取函数,移动到相关性高的类里面去。
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))); }
比较实用的一个方法。
相关文章推荐
- 31天重构学习笔记23. 引入参数对象
- Java方法参数太多怎么办—Part 2—引入参数对象
- Java方法参数太多怎么办—Part 2—引入参数对象
- 重构指南 - 引入参数对象(Introduce Parameter Object)
- 31天重构学习笔记23. 引入参数对象
- 小酌重构系列[6]——引入参数对象
- 31天重构学习笔记23. 引入参数对象
- 31天重构学习笔记23. 引入参数对象
- 31 天重构学习笔记23. 引入参数对象
- 重构手法50:Introduce Parameter Object (引入参数对象)
- 简化函数调用之九 :Introduce Parameter Object(引入参数对象)
- 引入参数对象
- 31天重构学习笔记23. 引入参数对象
- Introduce Parameter Object (引入参数对象)
- Introduce Parameter Object(引入参数对象)
- new 新对象,解决fun调用参数不同引入的闭包污染
- 第一道菜——引入对象参数
- 31天重构指南之二十三:引入参数对象
- VBA调用C#对象方法,传递double数组参数
- C++中关于将fstream对象作为函数参数传递相关问题说明