Introduce Parameter Object (引入参数对象)
2014-04-09 00:00
281 查看
Summary:某些参数总是很自然地同时出现。以一个对象取代这些参数。
动机:
你经常会看到特定的一组参数总是一起被传递。可能有好几个函数都使用这一组参数,这些函数可能隶属于同一个类,也可能隶属于不同的类。这样一组参数就是所谓的Data Clumps(数据泥团),我们可以运用给一个对象包装所有这些数据,再以该对象取代它们。哪怕只是为了把这些数据组织在一起,这样做也是值得的。本项重构的价值在于缩短参数列。而你知道,过长的参数列总是难以理解的。此外,新对象所定义的访问函数还可以是代码更具一致性,这又进一步降低了理解和修改代码的难度。
本项重构还可以带给你更多好处。但跟你把这些参数组织到一起之后,往往很快就可以发现一些可被移至新建类的行为。通常原本使用那些参数的函数对这一组参数会有一些共通的处理,如果将这些共通行为移到新对象中,你可以减少很多重复代码。
做法:
1.新建一个类,用以表现你想替换的一组参数。将这个类设为不可变的。
2.编译。
3.针对使用该组参数的所有函数,实施Add Parameter,传入上述新建类的实例对象,并将此参数值设为null。
à如果你所修改的函数被其他很多函数调用,那么可以保留修改前的旧函数,并令它调用修改后的新函数。你可以先对旧函数进行重构,然后逐一修改调用端使其调用新函数,最后再将旧函数删除。
4.对于Data Clumps中的每一项(在此均为参数),从函数签名中移除之,并修改调用端和函数本体,令它们都改而通过新的参数对象取得该值。
5.每去除一个参数,编译并测试。
6.将原先的参数全部去除之后,观察有无适当函数可以运用Move Method搬移到参数对象之中。
à被搬移的可能是整个函数,也可能是函数中的一个段落。如果是后者,首先使用Extract Method将该段落提炼为一个独立函数,再搬移这一新建函数。
范例:
下面是一个“账目和账项”范例。表示“账项”的Entry实际上只是个简单的数据容器:
我们关注的焦点是用以表示“账目”的Account,它保存了一组Entry对象,并有一个函数用来计算两个日期间的账项总量:
我们会经常看到代码用一对值来表示一个范围,例如表示日期范围的start和end、表示数值范围的upper和lower等等,我们应该尽量以“范围对象”取而代之。第一个步骤是声明一个简单的数据容器,用以表示范围:
把DateRange设为不可变,也就是说,其中所有字段都是final,只能由构造函数来赋值,因此没有任何函数可以修改其中任何字段值。这是一个明智的决定,因为这样可以避免别名带来的困扰。Java的函数参数都是按值传递的,不可变类正好能够模仿Java参数的工作方式,因此这种做法对于本项重构是最合适的。
接下来我们把DataRange对象加到getFlowBetween()函数的参数列中:
至此,只需要编译一下就行了,因为我们尚未修改程序的任何行为。
下一个步骤是去除旧参数之一,以新建对象取而代之。首先删除start参数,并修改getFlowBetween()函数及其调用者,让它们转而使用新对象:
然后,将end参数也移除:
现在,我们已经引入了参数对象。我们还可以将适当的行为从其他函数移到这个新建对象中,进一步bong本项重构获得更大利益。这里,我们选定条件表达式中的代码,实施Extract Method 和 Move Method,最后的到如下代码:
如此单纯的提炼和搬移动作通常可以一步完成。如果在这个过程中出错,可以回到重构前的状态,然后分成两个较小的步骤重新进行。
动机:
你经常会看到特定的一组参数总是一起被传递。可能有好几个函数都使用这一组参数,这些函数可能隶属于同一个类,也可能隶属于不同的类。这样一组参数就是所谓的Data Clumps(数据泥团),我们可以运用给一个对象包装所有这些数据,再以该对象取代它们。哪怕只是为了把这些数据组织在一起,这样做也是值得的。本项重构的价值在于缩短参数列。而你知道,过长的参数列总是难以理解的。此外,新对象所定义的访问函数还可以是代码更具一致性,这又进一步降低了理解和修改代码的难度。
本项重构还可以带给你更多好处。但跟你把这些参数组织到一起之后,往往很快就可以发现一些可被移至新建类的行为。通常原本使用那些参数的函数对这一组参数会有一些共通的处理,如果将这些共通行为移到新对象中,你可以减少很多重复代码。
做法:
1.新建一个类,用以表现你想替换的一组参数。将这个类设为不可变的。
2.编译。
3.针对使用该组参数的所有函数,实施Add Parameter,传入上述新建类的实例对象,并将此参数值设为null。
à如果你所修改的函数被其他很多函数调用,那么可以保留修改前的旧函数,并令它调用修改后的新函数。你可以先对旧函数进行重构,然后逐一修改调用端使其调用新函数,最后再将旧函数删除。
4.对于Data Clumps中的每一项(在此均为参数),从函数签名中移除之,并修改调用端和函数本体,令它们都改而通过新的参数对象取得该值。
5.每去除一个参数,编译并测试。
6.将原先的参数全部去除之后,观察有无适当函数可以运用Move Method搬移到参数对象之中。
à被搬移的可能是整个函数,也可能是函数中的一个段落。如果是后者,首先使用Extract Method将该段落提炼为一个独立函数,再搬移这一新建函数。
范例:
下面是一个“账目和账项”范例。表示“账项”的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对象,并有一个函数用来计算两个日期间的账项总量:
calss 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))){ reslut += each.getValue(); } } return reslut; } private Vector _entries = new Vector(); client code ... double flow = anAccount.getFlowBetween(startDate, endDate);
我们会经常看到代码用一对值来表示一个范围,例如表示日期范围的start和end、表示数值范围的upper和lower等等,我们应该尽量以“范围对象”取而代之。第一个步骤是声明一个简单的数据容器,用以表示范围:
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; }
把DateRange设为不可变,也就是说,其中所有字段都是final,只能由构造函数来赋值,因此没有任何函数可以修改其中任何字段值。这是一个明智的决定,因为这样可以避免别名带来的困扰。Java的函数参数都是按值传递的,不可变类正好能够模仿Java参数的工作方式,因此这种做法对于本项重构是最合适的。
接下来我们把DataRange对象加到getFlowBetween()函数的参数列中:
calss 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))){ reslut += each.getValue(); } } return reslut; } private Vector _entries = new Vector(); client code ... double flow = anAccount.getFlowBetween(startDate, endDate);
至此,只需要编译一下就行了,因为我们尚未修改程序的任何行为。
下一个步骤是去除旧参数之一,以新建对象取而代之。首先删除start参数,并修改getFlowBetween()函数及其调用者,让它们转而使用新对象:
calss 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))){ reslut += each.getValue(); } } return reslut; } private Vector _entries = new Vector(); client code ... double flow = anAccount.getFlowBetween(new DateRange(startDate,null), endDate);
然后,将end参数也移除:
calss 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()))){ reslut += each.getValue(); } } return reslut; } private Vector _entries = new Vector(); client code ... double flow = anAccount.getFlowBetween(new DateRange(startDate,endDate));
现在,我们已经引入了参数对象。我们还可以将适当的行为从其他函数移到这个新建对象中,进一步bong本项重构获得更大利益。这里,我们选定条件表达式中的代码,实施Extract Method 和 Move Method,最后的到如下代码:
calss 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())){ reslut += each.getValue(); } } return reslut; } private Vector _entries = new Vector(); class DateRange... boolean includes(Date arg){ return arg.equals(_start) || arg.equals(_end) || (arg.after(_start)&& arg.before(_end)); }
如此单纯的提炼和搬移动作通常可以一步完成。如果在这个过程中出错,可以回到重构前的状态,然后分成两个较小的步骤重新进行。
相关文章推荐
- 重构手法50:Introduce Parameter Object (引入参数对象)
- 重构指南 - 引入参数对象(Introduce Parameter Object)
- Introduce Parameter Object(引入参数对象)
- 简化函数调用之九 :Introduce Parameter Object(引入参数对象)
- 重构第23天 引用参数对象(Introduce Parameter Object)
- Introduce Null Object(引入Null对象)
- ActiveXObject 所支持的参数对象部分列表(自带)
- 在对象之间搬移特性之七 :Introduce Foreign Method(引入外加函数)
- 引入参数对象
- 在对象之间搬移特性之八 :Introduce Local Extension(引入本地扩展)
- 第一道菜——引入对象参数
- 31天重构学习笔记23. 引入参数对象
- new 新对象,解决fun调用参数不同引入的闭包污染
- [对象 performSelector:SEL withObject:id withObject:id]; 只有2个参数,而且是对象参数
- 黑马程序员-Object-C语言对象与函数参数的总结
- 重构23-Introduce Parameter Object(参数对象)
- Introduce Null Object(引入Null对象)
- json_decode参数,同时可以用于object对象转数组
- 小酌重构系列[6]——引入参数对象
- bean 和 Map 的相互转换(Mybatis 拦截器分页--将Object的参数对象封装到Pager 的params 中)