重构手法之处理概括关系【5】
本小节目录
11Replace Inheritance with Delegation(以委托取代继承)
概要
某个子类只使用基类接口中的一部分,或是根本不需要继承而来的数据。
在子类中新建一个字段用以保存基类;调整子类函数,令它改而委托基类;然后去掉两者之间的继承体系。
动机
使用继承时常常会遇到这样的问题:一开始继承了一个类,随后发现基类中的许多操作并不真正适用于子类。这种情况下,你所拥有的接口并未真正反映出子类的功能。或者,你可能发现你从基类中继承了一大堆子类并不需要的数据,抑或你可能发现基类中的某些protected函数对子类并没有意义。
如果以委托取代继承,可以更清楚地表明:你只需要受托类的一部分功能。接口中的哪一部分应该被使用,哪一部分应该被忽略,完全由你主导控制。这样做的成本则是需要额外写出委托函数,但这些函数都非常简单。
范例
如下代码所示,Child和Sanitation(公共设施)是没有逻辑上的父子关系,因为小孩不可能是一个公共设施吧!所以我们为了完成这个功能可以考虑使用委派的方式。
public class Sanitation { public string WashHands() { return "Cleaned!"; } } public class Child : Sanitation { }
重构后的代码如下,把Sanitation委派到Child类中,从而可以使用WashHands这个方法,这种方式我们经常会用到,其实IOC也使用到了这个原理,可以通过构造注入和方法注入等。
public class Sanitation { public string WashHands() { return "Cleaned!"; } } public class Child { private Sanitation _sanitation; public Child() { _sanitation = new Sanitation(); } public string WashHands() { return _sanitation.WashHands(); } }
小结
这个重构是一个很好的重构,在很大程度上解决了滥用继承的情况,很多设计模式也用到了这种思想(比如桥接模式、适配器模式、策略模式等)。
12Replace Delegation with Inheritance(以继承取代委托)
概要
你在两个类之间使用委托关系,并经常为整个接口编写许多极简单的委托函数。让委托类继承受托类。
动机
如果你发现自己需要受托类中的所有函数,并且花费很大力气编写所有极简单的委托函数,本重构可以帮助你轻松回头使用继承。
范例
下面是一个简单的Employee类,将一些函数委托给另一个同样简单的Person类:
class Employee { private Person _person = new Person(); public override string ToString() { return "Emp: " + _person.GetLastName(); } } class Person { public string Name { get; set; } public string GetLastName() { return Name.Substring(Name.LastIndexOf(' ') + 1); } }
重构后代码如下:
class Employee : Person { public override string ToString() { return "Emp: " + GetLastName(); } } class Person { public string Name { get; set; } public string GetLastName() { return Name.Substring(Name.LastIndexOf(' ') + 1); } }
小结
如果并没有使用受托类的所有函数,那么就不应该使用本重构,因为子类应该总是遵循基类的接口。
受托对象被不止一个其他对象共享,而且受托对象是可变的。在这种情况下,就不能将委托关系替换为继承关系,因为这样就无法再共享数据了。
阶段性小结
重构手法之处理概括关系【1】-【5】,顾名思义,专门用来处理类的概括关系即继承关系。
其中主要是将函数上下移动于继承体系中。Pull Up Field和Pull Up Method都用于将特性向继承体系的上端移动,Push Down Method和Push Down Field则将特性向继承体系的下端移动。构造函数比较难以向上拉动,因此专门有一个Pull Up Constructor Body处理它。一般不会将构造函数往下推,因为Replace Constructor with Factory Method通常更管用。
如果有若干函数大体上相同,只在细节上有所差异,可以使用Form Template Method将它们的共同点和不同点分开。
除了在继承体系中移动特性外,还可以建立新类,改变整个继承体系。Extract Subclass、Extract BaseClass和Extract Interface都是这样的重构手法,它们在继承体系中的不同位置构造出新元素。如果想在类型系统中标示一小部分函数,Extract Interface特别有用。如果发现继承体系中的某些类没有存在的必要,可以使用Collapse Hierarchy将它们移除。
有时候会发现继承并非是最佳选择,真正需要的其实是委托,那么Replace Inheritance with Delegation可以帮助把继承改为委托。有时候又会想要做反向修改,此时就可以使用Replace Delegation with Inheritance。
To Be Continued……