多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
### Pull Up Method(函数上移) 有些函数,在各个subclass 中产生完全相同的结果。 将该函数移至superclass。 ![](https://box.kancloud.cn/2016-08-15_57b1b5aa64f65.gif) **动机(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。 ![](https://box.kancloud.cn/2016-08-15_57b1b5e674fa9.gif) 两个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() 函数,再次编译并测试: ![](https://box.kancloud.cn/2016-08-15_57b1b5e699f6b.gif)