重构类关系-Pull Up Method函数上移二

重构类关系-Pull Up Method函数上移二

1.函数上移

1.1.使用场景

有些函数,在各个子类中产生完全相同的结果。将该函数移至超类。

避免行为重复是很重要的。尽管重复的两个函数也可以各自工作得很好,但重复自身只会成为错误的滋生地,此外别无价值。无论何时,只要系统之内出现重复,你就会面临“修改其中一个却未能修改另一个”的风险。通常,找出重复也有一定困难。

如果某个函数在各子类中的函数体都相同(它们很可能是通过复制粘贴得到的),这就是最显而易见的Pull Up Method (322)适用场合。当然,情况并不总是如此明显。你也可以只管放心地重构,再看看测试程序会不会发牢骚,但这就需要对你的测试有充分的信心。我发现,观察这些可能重复的函数之间的差异往往大有收获:它们经常会向我展示那些我忘记测试的行为。

Pull Up Method (322)常常紧随其他重构而被使用。也许你能找出若干个身处不同子类内的函数,而它们又可以通过某种形式的参数调整成为相同的函数。这时候,最简单的办法就是首先分别调整这些函数的参数,然后再将它们概括到超类中。当然,如果你足够自信,也可以一次完成这两个步骤。

有一种特殊情况也需要使用Pull Up Method (322):子类的函数覆写了超类的函数,但却仍然做相同的工作。
Pull Up Method (322)过程中最麻烦的一点就是:被提升的函数可能会引用只出现于子类而不出现于超类的特性。如果被引用的是个函数,你可以将该函数也一同提升到超类,或者在超类中建立一个抽象函数。在此过程中,你可能需要修改某个函数的签名,或建立一个委托函数。
如果两个函数相似但不相同,你或许可以先借助Form Template Method (345)构造出相同的函数,然后再提升它们。

1.2.如何做

  • 检查待提升函数,确定它们是完全一致的。
  • 如果这些函数看上去做了相同的事,但并不完全一致,可使用Substitute Algorithm (139)让它们变得完全一致。
  • 如果待提升函数的签名不同,将那些签名都修改为你想要在超类中使用的签名。
  • 在超类中新建一个函数,将某一个待提升函数的代码复制到其中,做适当调整,然后编译。
  • 如果你使用的是一种强类型语言,而待提升函数又调用了一个只出现于子类而未出现于超类的函数,你可以在超类中为被调用函数声明一个抽象函数。
  • 如果待提升函数使用了子类的一个字段,你可以使用Pull Up Field (320)将该字段也提升到超类;或者也可以先使用Self Encapsulate Field (171),然后在超类中把取值函数声明为抽象函数。
  • 移除一个待提升的子类函数。
  • 编译,测试。
  • 逐一移除待提升的子类函数,直到只剩下超类中的函数为止。每次移除之后都需要测试。
  • 观察该函数的调用者,看看是否可以改为使用超类类型的对象。

1.3.示例

我以Customer表示“顾客”,它有两个子类:表示“普通顾客”的Regular-Customer和表示“贵宾”的PreferredCustomer。
两个子类都有一个createBill()函数,并且代码完全一样:

 void createBill (date Date) {
    
    
   double chargeAmount = charge (lastBillDate, date);
   addBill (date, charge);
 }

但我不能直接把这个函数上移到超类,因为各个子类的chargeFor()函数并不相同。我必须先在超类中声明chargeFor()抽象函数:

 class Customer...
   abstract double chargeFor(date start, date end)

然后,我就可以将createBill()函数从其中一个子类复制到超类。复制完之后应该编译,然后移除那个子类的createBill()函数,再编译并测试。随后再移除另一个子类的createBill()函数,再次编译并测试:

猜你喜欢

转载自blog.csdn.net/m0_38039437/article/details/129748105