一个函数,它的参数过多是不好的,不好维护和修改,易读性也差,容易出错。
消除过长参数的方法,有如下:
1.在面向对象中,你可以传递一个对象给函数,函数通过访问对象来获得参数。也就是,对象里面了包含参数需要的多个参数。
2.函数通过访问函数所在类的成员变量,或者其它函数来获取原来要传入的参数。因为,有时候,是可以自己通过宿主类来获取需要的值,而不需要外部传入。
但是,并不是在任何情况下,都包装所有参数到一个对象中。有时候,是反过来,把参数从对象中拆解出来,因为,你不想要对象间的依赖。
相应的重构手法:
Replace Parameter with Method(用函数取代参数)
手法的含义:对象调用某个函数,并将所得结果作为参数,传递给另一个函数。而接受该参数的函数本身也能够调用前一个函数。那么,让参数接受者去除该项参数,并直接调用前一个函数。
例子:
public double getPrice() {
int basePrice = _quantity * _itemPrice; int discountLevel; if (_quantity > 100) discountLevel = 2; else discountLevel = 1; double finalPrice = discountedPrice (basePrice, discountLevel); return finalPrice; } private double discountedPrice (int basePrice, int discountLevel) { if (discountLevel == 2) return basePrice * 0.1; else return basePrice * 0.05; }
应用Replace Parameter with Method手法,重构后,结果是:将discountLevel参数消除掉,通过在函数体内添加参数值获取函数来实现。
public double getPrice() {
return discountedPrice (); } private double discountedPrice () { if (getDiscountLevel() == 2) return getBasePrice() * 0.1; else return getBasePrice() * 0.05; } private double getBasePrice() { return _quantity * _itemPrice; }
Preserve Whole Object(保持对象完整)
手法的含义:你从某个对象中取出若干值,将它们作为某一次函数调用时的参数。改为传递整个对象。
例子:
重构前:
HeatingPlan方法withinRange(int low, int high)。
我们的目的是,把这两个参数才为一个,修改为从从一个对象中获取。
class Room... boolean withinPlan(HeatingPlan plan) { int low = daysTempRange().getLow(); int high = daysTempRange().getHigh(); return plan.withinRange(low, high); } class HeatingPlan... boolean withinRange (int low, int high) { return (low >= _range.getLow() && high <= _range.getHigh()); } private TempRange _range;
修改后的效果:从TempRange对象获取值。
class HeatingPlan...
boolean withinRange (TempRange roomRange) { return (roomRange.getLow() >= _range.getLow() && roomRange.getHigh() <= _range.getHigh()); } class Room... boolean withinPlan(HeatingPlan plan) { int low = daysTempRange().getLow(); int high = daysTempRange().getHigh(); return plan.withinRange(daysTempRange()); }
再调整一下,因为_range也是一个TempRange类型的对象。在TempRange中添加是否包含范围的方法。
效果为:
class HeatingPlan...
boolean withinRange (TempRange roomRange) { return (_range.includes(roomRange)); } class TempRange... boolean includes (TempRange arg) { return arg.getLow() >= this.getLow() && arg.getHigh() <= this.getHigh();
}
Introduce Parameter Object(引入参数对象)
手法的含义:
某些参数总是很自然地同时出现。以一个对象取代这些参数。
例子:
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是成对出现的。可以用一个对象来代替它们
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 (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))); }
参考资料:
https://sourcemaking.com/refactoring/preserve-whole-object
https://sourcemaking.com/refactoring/replace-parameter-with-method
https://sourcemaking.com/refactoring/introduce-parameter-object