重构改善既有代码设计--重构手法16:Introduce Foreign Method (引入外加函数)&& 重构手法17:Introduce Local Extension (引入本地扩展)

重构手法16:Introduce Foreign Method (引入外加函数)
你需要为提供服务的类增加一个函数,但你无法修改这个类。在客户类中建立一个函数,并以第一参数形式传入一个服务类实例。

动机:这种事情发生了太多次了,你正在使用一个类,它真的很好,为你提供了需要的所有服务。而后,你又需要一项新服务,这个类却无法供应。于是你开始咒骂“为什么不能做这件事?”如果可以修改源码,你便可以自行添加一个新函数;如果不能,你就得在客户端编码,补足你要的那个函数。

如果客户类只使用这项功能一次,那么额外编码工作没什么大不了,甚至可能根本不需要原本提供服务的那个类。然而,如果你需要多次使用这个函数,就得不断重复这些代码。重复代码是软件万恶之源。这些重复代码应该被抽出来放进一个函数中。进行本项重构时,如果你以外加函数实现一项功能,那就是一个明确信号:这个函数原本应该在提供服务的类中实现。

如果你发现自己为一个服务类建立了大量外加函数,或者发现有许多类需要同样的外加函数,就不应该再使用本项重构,而应该使用 Introduce Local Extension (引入本地扩展)。

但是不要忘记:外加函数终归是权宜之计。如果有可能,你仍然应该将这些函数搬移到它们的理想家园。如果由于代码所有权的原因使你无法做这样的搬移,就把外加函数交给服务类的提供者,请他帮你在服务类中实现这个函数。

做法:1、在客户类中建立一个函数,用来提供你需要的功能。这个函数不应该调用客户类的任何特性。如果它需要一个值,把该值动作参数传给它。

2、以服务类实例作为该函数的第一个参数。

3、将该函数注释为:”外加函数“,应在服务类中实现。这么一来,如果将来有机会将外加函数搬移到服务类时,你便可以轻松找出这些外加函数。

重构手法17:Introduce Local Extension (引入本地扩展)

你需要为服务类提供一些额外函数,但你无法修改这个类。建立一个新类,使它包含这些额外函数。让这个扩展品成为源类的子类或包装类。

动机:类的作者无法预知未来,他们常常没能为你预先准备一些有用的函数。如果你可能修改源码,最后的办法就是直接加入自己需要的函数。但你经常无法修改源码。如果只需要一两个函数,你可以使用 Introduce Foreign Method (引入外加函数)。但如果你需要的额外函数超过2个,外加函数就很难控制它们了。所以你需要将这些函数组织在一起,放到一个恰当的地方去。要达到这个目的,2种标准对象技术—子类化(subclassing)和包装(wrapping)是显而易见的办法。这种情况下,把子类化和包装类统称为本地扩展。

所谓本地扩展是一个独立的类,但也是被扩展类的字类型:它提供源类的一切特性,同时额外添加新特性。在任何使用源类的地方,你都可以使用本地扩展取而代之。

使用本地扩展使你得以坚持:函数和数据应该被统一封装“的原则。如果你一直把本该放在扩展类中的代码零散的放置于其他类中,最终只会让其他这些类变得过分复杂,并使得其他函数难以被复用。

在子类和包装类之间做选择时,首选子类。因为这样的工作量比较少。制作子类的最大障碍在于,它必须在对象创建期实施。如果可以接管对象创建过程,那当然没问题;但如果你想在对象创建之后再使用本地扩展,就有问题了。此时,子类化方案还必须产生一个子类对象,这种情况下,如果有其他对象引用了旧对象,我们就同时有2个对象保存了原数据。如果原数据是不可修改的,那也没问题。可以放心进行复制;但如果原数据允许修改,问题就来了,因为一个修改动作无法同时改变2份副本。这时候就必须改用包装类。使用包装类时,对本地扩展的修改会波及原对象,反之亦然。

做法:1、建立一个扩展类,将它作为原始类的子类或包装类。

2、在扩展类中加入转型构造函数。所谓“转型构造函数“是指”接受原对象作为参数“的构造函数。如果采用子类化方案,那么转型构造函数应该调用适当的超类构造函数。如果采用包装类,那么转型构造函数应该将它得到的传入参数以实例变量的形式保存起来,用做接受委托的原对象。

3、在扩展类中加入新特性。

4、根据需要,将原对象替换为扩展对象。

5、将针对原始定义的所有外加函数搬移到扩展类中。

时间: 2024-08-08 17:51:26

重构改善既有代码设计--重构手法16:Introduce Foreign Method (引入外加函数)&& 重构手法17:Introduce Local Extension (引入本地扩展)的相关文章

重构改善既有代码设计--重构手法05:Introduce Explaining Variable (引入解释性变量)

  发现:你有一个复杂的表达式. 解决:将该复杂的表达式(或其中的部分)的结果放进一个临时变量,并以此变量名称来解释表达式用途. //重构前 if((platform.toUpperCase().indexOf("MAC") > -1) && (browser.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize > 0) {

重构改善既有代码设计--重构手法04:Replace Temp with Query (以查询取代临时变量)

所谓的以查询取代临时变量:就是当你的程序以一个临时变量保存某一个表达式的运算效果.将这个表达式提炼到一个独立函数中.将这个临时变量的所有引用点替换为对新函数的调用.此后,新函数就可以被其他函数调用. 例子如下: double basePrice = _quantity*_itemPrice; if (basePrice > 1000) { return basePrice * 0.95; } else { return basePrice * 0.98; } 重构之后代码: if (BasePr

重构改善既有代码设计--重构手法 之重新组织你的函数总结

前面讲了那么多的重构手法,估计学完后都会忘记,因此暂停下脚本,总结下,其实前面的所有重构手段,围绕一个主题:函数.即对函数的优化:为此首先一个函数里面代码很长,那么此时我们要做的就是提炼函数. 提炼函数的过程中,我们会产生新的函数,这个时候最重要的就是,是否有变量的引用,如果没有,那么这个函数很好提炼:如果有,那么就看是否会在子函数或者说新拆的函数中会对其赋值操作,如果没有赋值操作,那么也很简单,那么就直接将这个变量以参数的形式传入进去.那么最麻烦的就是,在拆出来的函数中会引用到原函数中的变量:

重构改善既有代码设计--重构手法01:Extract Method (提炼函数)

背景: 你有一段代码可以被组织在一起并独立出来.将这段代码放进一个独立函数,并让函数名称解释该函数的用途. void PrintOwing(double amount) { PrintBanner(); //print details Console.WriteLine("name:"+_name); Console.WriteLine("amount:"+_amount); } void PrintOwing(double amount) { PrintBanne

重构改善既有代码设计--重构手法07:Remove Assignments to Parameters (移除对参数的赋值)

代码对一个 参数赋值.以一个临时变量取代该参数的位置.     int Discount(int inputVal, int quantity, int yearTodate) { if (inputVal > 50) { inputVal -= 2; } } 重构后: int Discount(int inputVal, int quantity, int yearTodate) { int result=inputVal; if (inputVal > 50) { result -= 2;

重构改善既有代码设计--重构手法08:Replace Method with Method Object (以函数对象取代函数)

你有一个大型函数,其中对局部变量的使用,使你无法釆用 Extract Method. 将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的值域(field) 然后你可以在同一个对象中将这个大型函数分解为数个小型函数. class Order... double price() { double primaryBasePrice; double secondaryBasePrice; double tertiaryBasePrice; // long computation; ... }

重构改善既有代码设计--重构手法02:Inline Method (内联函数)& 03: Inline Temp(内联临时变量)

Inline Method (内联函数) 一个函数调用的本体与名称同样清楚易懂.在函数调用点插入函数体,然后移除该函数. int GetRating() { return MoreThanfiveLateDeliverise() ? 2 : 1; } bool MoreThanfiveLateDeliverise() { return _numberOfLateLiveries > 5; } int GetRating() { return _numberOfLateLiveries > 5

重构改善既有代码设计--重构手法19:Replace Data Value with Object (以对象取代数据值)

你有一笔数据项(data item),需要额外的数据和行为. 将这笔数据项变成一个对象. class Order... private string customer; ==> class Order... private Customer _customer; class Customer... private string _name; 动机 一开始你可能会用一个字符串来表示[电话号码]概念,但是随后你就会发现,电话号码需要[格式化].[抽取区号]之类的特殊行为.当这些臭味开始出现,你就应该

重构改善既有代码设计--重构手法09:Substitute Algorithm (替换算法)

你想要把某个算法替换为另一个更清晰地算法.将函数本体替换为另一个算法. string FoundPerson(string[] people) { for (int i = 0; i < people.Length; i++) { if (people[i].Equals("don")) { return "don"; } if (people[i].Equals("john")) { return "john"; }