《重构改善既有代码的设计》之重构列表--处理概括关系(三)

来源:互联网 发布:司马南被夹头 知乎 编辑:程序博客网 时间:2024/05/16 15:32

九、Collapse Hierarchy(折叠继承体系)

超类和子类之间无太大区别。

将它们合为一体。

动机

如果你曾经编写过继承体系,就会知道,继承体系很容易变得过分复杂。所谓重构继承体系,往往是将函数和字段在体系中上下移到。完成这些动作后,你狠可能发现某个子类并未带来该有的价值,因此需要把超类和子类合并起来。

做法

1、选择你想移除的类:是超类还是子类?

2、使用Pull Up Field 和 Pull Up Method,或者Push Down FieldPush Down Method,把想要移除的类的所有行为和数据搬移到另一个类。

3、每次移动后,编译并测试。

4、调整即将被移除的那个类的所有引用点,令它们改而引用合并后留下的那个类。

。这个动作将会影响变量的声明、参数的类型以及构造函数。

5、移除我们的目标:此时的它应该已经成为一个空类。

6、编译,测试。

十、Form Template Method(塑造模板函数)

你有一些类,其中相应的某些函数以相同顺序执行类似的操作,但各个操作的细节上有所不同。

将这些操作分别放进独立函数中,并保持它们都有相同的签名,于是原函数也就变得相同了。然后将原函数上移至超类。

动机

继承是避免重复行为的一个强大工具。无论何时,只要你看见两个子类中有类似的函数,就可以把它们提升到超类。但是如果这些函数并不完全相同该怎么办?我们仍有必要尽量避免重复,但又必须保持这些函数之间的实质差异。

常见的一种情况是:两个函数以相同顺序执行大致相近的操作,但是各个操作不完全相同。这种情况下我们可以将执行操作的序列移至超类,并借助多态保证各操作仍得以保持差异性。这样的函被称为Template Method(模板函数)

做法

1、在各个子类中分解目标函数,使分解后的各个函数要不完全相同,要不完全不同。

2、运用Pull Up Method将各子类内完全相同的函数上移至超类。

3、对于那些(剩余的、存在于各个子类内的)完全不同的函数,实施Rename Method,使所有的这些函数的签名完全不同。

这将使得原函数变为完全相同,因为它们都执行统一一组函数调用;但各子类会以不同方式相应这些调用。

4、修改上述所有签名后,编译并测试。

5、运用Pull Up Method将所有原函数逐一上移至超类。在超类中将那些代表各个不同操作的函数定义为抽象函数。

6、编译,测试。

7、移除其他子类中的原函数,每删除一个,编译并测试。

十一、Replace Inheritance with Delegation(以委托取代继承)

某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。

在子类中新建字段用以保存超类;调整子类函数,令它改而委托超类;然后去掉两者之间的继承关系。

动机

继承是个好东西,但有时候它并不是你要的。你常常会遇到这样的情况:一开始继承了一个类,随后发现超类中的许多操作并不真正适用于子类。这种情况下,你所拥有的接口并未真正反映出子类的功能。或者,你可能发现你从超类中继承了一大堆子类并不需要的数据,抑或你发现超类中的某些protected函数对子类并没有什么意义。

你可以选择容忍,并接受传统说法:子类可以只使用超类功能的一部分。但这样做的结果是:代码传达的信息与你的意图南辕北辙---这是一种混淆,你应该将它去除。

如果以委托取代继承,你可以更清楚地表明:你只需要受托类的一部分功能。接口中的那一部分应该被使用,哪一部分应该被忽略,完全由你主导控制。这样做的成本则是需要额外写出委托函数,但这些函数都非常简单,极少可能出错。

做法

1、在子类中新建一个字段,使其引用超类的一个实例,并将它初始化为this.

2、修改子类内的所有函数,让它们不再使用超类,转而使用上述那个受托字段。每次修改后,编译并测试。

你不能这样修改子类中通过super调用的超类函数的代码,否则它们会陷入无限递归。这种函数只有在继承体系被打破后才能修改。

3、去除两个类之间的继承关系,新建一个受托类的对象赋给受托字段。

4、针对客户端所用的每一个超类函数,为它添加一个简单的受托函数。

5、编译,测试。

十二、Replace Delegation with Inheritance(以继承取代委托)

你在两个类之间使用委托关系,并经常为整个接口编写许多简单的委托函数。

让委托类继承受托类。

动机

本重构与Replace Inheritance with Delegation恰恰相反。如果你发现自己需要使用受托类中的所有函数,并且费了很大力气编写所有极简的委托函数,本重构可以帮助你轻松回头使用继承。

两条告诫需牢记于心。首先,如果你并没有使用受托类的所有函数,那么就不应该使用Replace Delegation with Inheritance,因为子类应该总是遵循超类的接口。如果过多的委托函数让你烦心,你有别的选择:你可以通过Remove Middle Man容乃公客户端自己调用受托函数,也可以使用Extract Superclass将两个类接口相同的部分提炼到超类中,然后让两个类都继承这个新的超类;你还可以用类似的手法使用Extract Interface

另一种需要当心的情况是:受托对象被不止一个其他对象共享,而且受托对象是可变的。在这种情况下,你就不能将委托关系替换为继承关系,因为这样就无法再共享数据了。数据共享是必须由委托关系承担的一种责任,你无法把它转给继承关系。如果受托对象是可变的,数据共享就不成问题,因为你大可放心的复制对象,谁都不会知道。

做法

1、让委托端成为受托端的一个子类。

2、编译。

此时,某些函数可能会发生冲突:它们可能有相同的名称,但在返回类型、异常指定或可见程度方面有所差异。你可以使用Rename Method解决此类问题。、

3、将受托字段设为该字段所处对象本身。

4、去掉简单的委托函数。

5、编译并测试。

6、将所有其他涉及委托关系的代码,改为调用对象自身。

7、移除受托字段。

原创粉丝点击