处理概括关系之二 :Pull Up Method(函数上移)

来源:互联网 发布:xilinx ise软件 编辑:程序博客网 时间:2024/05/01 17:42

有些函数,在各个subclass 中产生完全相同的结果。

将该函数移至superclass。

动机(Motivation)

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

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

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

有一种特殊情况也需要使用Pull Up Method : subclass 的函数覆写(overrides) 了superclass 的函数,但却仍然做相同的工作。

Pull Up Method 过程中最麻烦的一点就是:被提升的函数可能会引用「只出现于subclass 而不出现于superclass」的特性。如果被引用的是个函数,你可以将该函数也一同提升到superclass,或者在superclass 中建立一个抽象函数。在此过程中,你可能需要修改某个函数的签名式(signature),或建立一个委托函数(delegating method)。

如果两个函数相似但不相同,你或许可以先以Form Template Method 构造出相同的函数,然后再提升它们。

作法(Mechanics)

·检查「待提升函数」,确定它们是完全一致的(identical)。
Ø如果这些函数看上去做了相同的事,但并不完全一致,可使用Substitute Algorithm 让它们变得完全一致。

·如果「待提升函数」的签名式(signature)不同,将那些签名式都修改为你想要在superclass 中使用的签名式。

·在superclass 中新建一个函数,将某一个「待提升函数」的代码拷贝到其中,做适当调整,然后编译。

Ø如果你使用的是一种强型(strongly typed)语言,而「待提升函数」 又调用了一个「只出现于subclass 未出现于superclass」的函数,你可以在superclass 中为被调用函数声明一个抽象函数。

Ø如果「待提升函数」使用了 subclass 的一个值域,你可以使用Pull Up Field 将该值域也提升到superclass;或者也可以先使用 Self Encapsulate Field,然后在superclass 中把取值函数(getter)声明为抽象函数。

·移除一个「待提升的subclass 函数」。

·编译,测试。

·逐一移除「待提升的如函数」,直到只剩下superclass 中的函数为止。每次移除之后都需要测试。

·观察该函数的调用者,看看是否可以将它所索求的对象型别改为superclass。

范例:(Example)

我以Customer「表示「顾客」,它有两个subclass  :表示「普通顾客」的RegularCustomer 和表示「贵宾」PreferredCustomer。

两个subclass 都有一个createBill() 函数,并且代码完全一样:

void createBill (date Date) {

   double chargeAmount = charge (lastBillDate, date);

   addBill (date, charge);

}

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

class Customer...

   abstract double chargeFor(date start, date end)

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

原创粉丝点击