本文主要是介绍《重构改善既有代码的设计》之重构列表--处理概括关系(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
有一批重构手法专门用来处理概括关系(既继承关系)。
一、Pull Up Field(字段上移)
两个子类拥有相同的字段。
将该字段移至超类中。
动机
如果各子类是分别开发的,或者是在重构过程中组合起来的,你常会发现他们拥有重复特性,特别是字段更容易重复,这样的字段有时拥有相似的名字,但也并非绝对如此。判断若干字段是否重复,唯一的办法是观察函数如果使用它们。如果它们被使用的方式相似,你就可以将它们归纳到超类中。
本项重构从两方面减少重复:首先它去除了重复的数据声明;其次它使你可以将使用该字段的行为从子类一直超类,从而去除重复行为。
做法
1、针对待提升之字段,检查它们的所有被引用点,确认它们以同样的方式被使用。
2、如果这些字段的名称不同,先将它们改名,使每一个名称都和你想为超类字段取的名称相同。
3、编译并测试。
4、在超类中新建一个字段。
5、移除子类中的字段。
6、编译,测试。
7、考虑对超类的新建字段使用Self Encapsulate Field。
二、Pull Up Method(函数上移)
有些函数,在各个子类中产品完全相同的结果。
将该函数移至超类。
动机
避免行为重复是很重要的。尽管重复的两个函数也可以很好的工作,但重复自身只会成为错误的滋生地,此外别无价值。无论何时,只有系统之内出现重复,你就会面临“修改其中一个却未能修改另一个”的风险。通常,找出重复也有一定的困难。
如果某个函数在各子类中的函数体都相同(它们很可能是通过复制粘贴得到的),这就是最显而易见的Pull Up Method适用场合。当然,情况并不总是如此明显。你也可以只管放心重构,再看看测试程序会不会发牢骚,但这就需要对你的测试有充分的信心。我发现观察这些可能重复的函数之间的差异往往大有收获:它们经常会向我们展示那些我忘记测试的行为。
Pull Up Method常常紧随其他重构而被使用。也许你能找出若干个身处不同子类内的函数,而它们又可以通过某种形式的参数调整成为相同的函数。这时候,最简单的办法就是首先分别调整这些函数的参数,然后再将它们概括到超类中。当然,如果你足够自信,也可以一次完成这两个步骤。
有一种特殊情况也需要使用Pull Up Method:子类的函数覆写了超类的函数,但却仍然做相同的工作。
Pull Up Method过程中最麻烦的一点是:被提升的函数可能会引用只出现于子类而不出现于超类的特性。如果被引用的是个函数,你可以将该函数也一同提升到超类中,或者在超类中建立一个抽象函数。在此过程中,你可能需要修改某个函数的签名,或建立一个委托函数。
如果两个函数相似但不相同,你或许可以先借助Form Template Method构造出相同的函数,然后再提升它们。
做法
1、检查待提升函数,确定它们是完全一致的。
? 如果这些函数看上去做了相同的事,但并非完全一致,可使用Substitute Algorithm 让它们变得完全一致。
2、如果待提升函数的签名不同,将那些签名都修改为你想要在超类中使用的签名。
3、在超类中新建一个函数,将某一个待提升的函数的代码复制到其中,做适当的调整,然后编译。
? 如果你使用的是一种强类型语言,而待提升函数又调用了一个只出现于子类而未出现于超类的函数,你可以在超类中为被调用函数声明一个抽象函数。
? 如果待提升函数使用了子类的一个字段,你可以使用Pull Up Field将该字段提升到超类中;或者也可以先使用Self Encapsulate Field,然后在超类中把取值函数声明为抽象函数。
4、移除一个待提升子类函数。
5、编译、测试。
6、逐一移除待提升的子类函数,直到只剩下超类中的函数为止。每次移除后都需要测试。
7、观察该函数的调用者,看看是否可以改为使用超类类型的对象。
三、Pull Up Constructor Body(构造函数本体上移)
你在各个子类中拥有一些构造函数,它们的本体几乎完全一致。
在超类中新建一个构造函数,并在子类构造函数中调用它。
动机
构造函数是很奇妙的东西。它们不会普通的函数,使用它们比使用普通函数受到更多的限制。
如果你看到各个子类的函数有共同的行为,第一个念头就是将共同行为提炼到一个独立函数中,然后将这个函数提升到超类。对构造函数而言,它们彼此的共同行为往往就是“对象的建构”。这时候你需要在超类中提供一个构造函数,然后让子类都来调用它。很多时候,子类构造函数的唯一动作就是调用超类构造函数。这里不能运用Pull Up Method ,因为你无法在子类中继承超类构造函数。
如果重构过程过于复杂,你可以先考虑使用Replace Constructor with Factory Method。
做法
1、在超类中定义一个构造函数。
2、将子类构造函数中的共同代码搬移到超类构造函数中。
? 被搬移的可能是子类构造函数的全部内容。
? 首先设法将共同代码搬移到子类构造函数中,然后再复制到超类构造函数中。
3、将子类构造函数中的共同代码删掉,改而调用新建的超类构造函数。
? 如果子类构造函数中的所有代码都是一样的,那么子类构造函数就只需要调用超类构造函数。
4、编译、测试。
? 如果日后子类构造函数再出现相同代码,你可以首先使用Extract Method将那部分提炼到一个独立函数中,然后使用Pull Up Method将该函数上移到超类。
四、Push Down Method(函数下移)
超类中的某个函数只与部分(而非全部)子类有关。
将这个函数移到相关的子类去。
动机
Push Down Method 与 Pull Up Method恰恰相反。当我有必要把某些行为从超类移至特定的子类时,我就使用 Push Down Method,它通常也只有在这种时候使用。使用Extract Subclass之后你可能会需要它。
做法
1、在所有子类中声明该函数,将超类中的函数本体复制到每一个子类函数中。
? 你可能需要将超类的某些字段声明为protected,让子类函数也能够访问他们。如果日后你也想把这些字段下移到子类,通常就可以那么做;否则应该使用超类提供的访问函数。如果访问函数并非public,你得将它声明为protected。
2、删除超类中的函数
? 你可能必须修改调用端的某些变量声明或参数声明,以便能够使用子类。
? 如果有必要通过一个超类对象访问该函数,或你不想把该函数从任何子类中移除,再或超类是抽象类,那么你就可以在超类中把该函数声明为抽象函数。
3、编译、测试。
4、将该函数从所有不需要它的那些子类中删除。
5、编译、测试。
这篇关于《重构改善既有代码的设计》之重构列表--处理概括关系(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!