简化函数调用之七 :Preserve Whole Object(保持对象完整)

来源:互联网 发布:联合办公网络设计方案 编辑:程序博客网 时间:2024/06/06 19:51

你从某个对象中取出若干值,将它们作为某一次函数调用时的参数。

改使用(传递)整个对象。

     int low = daysTempRange().getLow();

     int high = daysTempRange().getHigh();

     withinPlan = plan.withinRange(low, high);

 

     withinPlan = plan.withinRange(daysTempRange());

动机(Motivation)

有时候,你会将来自同一对象的若干项数据作为参数,传递给某个函数。这样做的问题在于:万一将来被调用函数需要新的数据项,你就必须查找并修改对此函数的所有调用。如果你把这些数据所属的整个对象传给函数,可以避免这种尴尬的处境, 因为被调用函数可以向那个参数对象请求任何它想要的信息。

除了可以使参数列更稳固(不变动)之外,Preserve Whole Object 往往还能提高代码的可读性。过长的参数列很难使用,因为调用者和被调用者都必须记住这些参数的用途。此外,不使用完整对象也会造成重复代码,因为被调用函数无法利用完整对象中的函数来计算某些中间值。

「甘蔗不曾两头甜」!如果你传的是数值,被调用函数就只与这些数值有依存关系(dependency),与这些数值所属对象没有任何依存关系。但如果你传递的是整个对象,「参数对象」和「被调用函数所在对象」之间,就有了依存关系。如果这会使你的依存结构恶化,那么你就不该使用Preserve Whole Object。

我还听过另一种不使用Preserve Whole Object 的理由:如果被调用函数只需要「参数对象」的其中一项数值,那么只传递那个数值会更好。我并不认同这种观点,因为传递一项数值和传递一个对象,至少在代码清晰度上是等价的〔当然对于pass by value(传值)参数来说,性能上可能有所差异)。更重要的考量应该放在「对象之间的依存关系」上。

如果被调用函数使用了 [来自另一个对象的很多项数据」,这可能意味该函数实际上应该被定义在「那些数据所属的对象」中。所以,考虑Preserve Whole Object 的同时,你也应该考虑Move Method。

运用本项重构之前,你可能还没有定义一个完整对象。那么你就应该先使用Introduce Parameter Object。

还有一种常见情况:调用者将自己的若干数据作为参数,传递给被调用函数。这种情况下,如果该对象有合适的取值函数(getter),你可以使用取代这些参数值,并且无须操心对象依存问题。

作法(Mechanics)

·对你的目标函数新添一个参数项,用以代表原数据所在的完整对象。

·编译,测试。

·判断哪些参数可被包含在新添的完整对象中。

·选择上述参数之一,将「被调用函数」内对该参数的各个引用,替换为「对新添之参数对象的相应取值函数(getter)」的调用。

·删除该项参数。

·编译,测试。

·针对所有「可从完整对象中获得」的参数,重复上述过程。

·删除调用端中那些带有「被删除之参数」的所有代码。
Ø当然,如果调用端还在其他地方使用了这些参数,就不要删除它们。

·编译,测试。

范例:(Example)

以下范例,我以一个Room 对象表示「房间」,它负责记录房间一天中的最高温度和最低温度。然后这个对象需要将「实际温度范围」与预先规定的「温度控制计划」 相比较,告诉客户当天温度是否符合计划要求:

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 对象的信息拆开来单独传递,只需将整个对象传递给withinPlan() 函数即可。在这个简单的例子中,我可以一次性完成修改。如果相关的参数更多些,我也可以进行小步重构。首先,我为参数列添加新的参数项,用 以传递完整的TempRange 对象:

class HeatingPlan...

   boolean withinRange (TempRange roomRange, int low, int high) {

       return (low >= _range.getLow() && high <= _range.getHigh());

   }

class Room...

   boolean withinPlan(HeatingPlan plan) {

       int low = daysTempRange().getLow();

       int high = daysTempRange().getHigh();

       return plan.withinRange(daysTempRange(), low, high);

   }

然后,我以TempRange 对象提供的函数来替换low 参数:

class HeatingPlan...

   boolean withinRange (TempRange roomRange, int high) {

       return (roomRange.getLow() >= _range.getLow() && high <= _range.getHigh());

   }

class Room...

   boolean withinPlan(HeatingPlan plan) {

       int low = daysTempRange().getLow();

       int high = daysTempRange().getHigh();

       return plan.withinRange(daysTempRange(), high);

   }

重复上述步骤,直到把所有待处理参数项都去除为止:

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());

   }

现在,我不再需要low 和high 这两个临时变量了:

class Room...

   boolean withinPlan(HeatingPlan plan) {

       int low = daysTempRange().getLow();

       int high = daysTempRange().getHigh();

       return plan.withinRange(daysTempRange());

   }

使用完整对象后不久,你就会发现,可以将某些函数移到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();

原创粉丝点击