ivaneeo's blog

自由的力量,自由的生活。

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  669 Posts :: 0 Stories :: 64 Comments :: 0 Trackbacks

#

范例(Examples)
下面是Account class的部分代码:
class Account...
    private AccountType _type;
    private double _interestRate;
    double interestForAmount_days(double amount, int days) {
       return _interestRate * amount * days / 365;
    }
我想把表示利率的_interestRate搬移到AccountType class去。目前已有数个函数引用了它,interestForAmount_days()就是其一。下一步我要在AccountType中建立_interestRate field以及相应的访问函数:
class AccountType...
    private double _interestRate;

    void setInterestRate(double arg) {
       _interestRate = arg;
    }
    double getInterestRate() {
       return _interestRate;
    }

这时候我可以编译新的 AccountType class。
现在,我需要让Account class中访问_interestRate field的函数转而使用AccountType对象,然后删除Account class中的_interestRate field。我必须删除source field,才能保证其访问函数的确改变了操作对象,因为编译器会帮我指出未正确获得修改的函数。
    private double _interestRate;
    double interestForAmount_days(double amount, int days) {
       return _type.getInterestRate() * amount * days / 365;
    }
posted @ 2005-08-30 14:46 ivaneeo 阅读(186) | 评论 (0)编辑 收藏

作法(Mechanics)
    • 如果field的属性是public,首先使用Encapsulate Field(206)将它封装起来。
        • ==》如果你有可能移动那些频繁访问该field的函数,或如果有许多函数访问某个field,先使用Self Encapsulate Field(171)也许会有帮助。
    • 编译,测试。
    • 在target class中建立与source field相同的field,并同时建立相应的设值/取值(setting/getting)函数。
    • 编译target class。
    • 决定如何在source object中引用target object。
        • ==》一个现成的field或method可以助你得到target object。如果没有,就看能否轻易建立这样一个函数。如果还不行,就得在source class中新建一个field来存放target object。这可能是个永久性修改,但你也可以暂不公开它,因为后续重构可能会把这个新建field除掉。
    • 删除source field。
    • 将所有[对source field的引用]替换为[对target适当函数的调用]。
        • ==》如果是[读取]该变量,就把[对source field的引用]替换为[对target取值函数(getter)的调用];如果是[赋值]该变量,就把[对source field的引用]替换成[对设值函数(setter)的调用]。
        • ==》如果source field不是private,就必须在source class的所有subclasses中查找source field的引用点,并进行相应替换。
    • 编译,测试。
posted @ 2005-08-30 14:31 ivaneeo 阅读(150) | 评论 (0)编辑 收藏

动机(Motivation)
如果我发现,对于一个field(值域),在其所驻class之外的另一个class中有更多函数使用了它,我就会考虑搬移这个field。上述所谓[使 用]可能通过设值/取值(setting/getting)函数间接进行。我也可能移动该field的用户(某函数),这取决于是否需要保持接口不受变 化。如果这些函数看上去很合适待在原地,我就选择搬移field。

使用Extract Class(149)时,我也可能需要搬移field。此时我会先搬移field,然后再搬移函数。
posted @ 2005-08-30 14:15 ivaneeo 阅读(141) | 评论 (0)编辑 收藏

你的程序中,某个field(值域)被其所驻class之外的另一个class更多地用到。

在target class建立一个new field,修改source field的所有用户,令它们改用new field。

Move_Field.png
posted @ 2005-08-30 13:55 ivaneeo 阅读(158) | 评论 (0)编辑 收藏

范例(Examples)
我用一个表示[帐户]的account class来说明这项重构:
class Account...
    double overdraftCharge() {   //透支金计费,它和其他class的关系似乎比较密切。
       if(_type.isPremium()) {
          double result = 10;
          if(_daysOverdrawn > 7)
             result += (_daysOverdrawn -7) * 0.85;
          return result;
       }
       else return _daysOverdrawn * 1.75;
    }
    double bankCharge() {
       double result = 4.5;
       if(_daysOverdrawn > 0) result += overdraftCh你arge();
       return result;
    }
    private AccountType _type;
    private int _daysOverdrawn;

假设有数种新帐户,每一种都有自己的[透支金计费规则]。所以我希望将overdraftCharge()搬移到AccountType class去。

第一步要做的是:观察被overdraftCharge()使用的每一特性(features),考虑是否值得将它们与overdraftCharge()一起移动。此例之中我需要让_daysOverdrawn值域留在Account class,因为其值会随不同种类的帐户而变化。然后,我将overdraftCharge()函数码拷贝到AccountType中,并做相应调整。
class AccountType...
    double overdraftCharge(int daysOverdrawn) {
       if(isPremium()) {
          double result = 10;
          if(daysOverdrawn >7)
             result += (daysOverdrawn - 7) * 0.85;
          return result;
        }
       else return daysOverdrawn * 1.75;
    }
在这个例子中,[调整]的意思是:(1)对于[使用AccountType特性]的语句,去掉──type;(2)想办法得到依旧需要的Account class特性。当我需要使用source class特性,我有四种选择:(1)将这个特性也移到target class;(2)建立或使用一个从target class到source的引用(指涉)关系;(3)将source object当作参数传给target method;(4)如果所需特性是个变量,将它当作参数传给target method。

本例中我将_daysOverdrawn变量作为参数传给target method(上述(4))。

调整target method使之通过编译,而后我就可以将source method的函数本体替换为一个简单的委托动作(delegation),然后编译并测试:
    class Account...
       double overdraftCharge() {
          return _type.overdraftCharge(_daysOverdrawn);
       }
我可以保留代码如今的样子,也可以删除source method。如果决定删除,就得找出source method的所有调用者,并将这些调用重新定向,改调用Account的bankCharge():
bankCharge():
    class Account...
       double bankCharge() {
          double result = 4.5;
          if(_daysOverdrawn > 0)
             result += _type.overdraftCharge(_daysOverdrawn);
          return result;
        }
所有调用点都修改完毕后,我就可以删除source method在Account中的声明了。我可以在每次删除之后编译并测试,也可以一次性批量完成。如果被搬移的函数不是private,我还需要检查其 他classes是否使用了这个函数。在强型(strongly typed)语言中,删除source method声明式后,编译器帮我发现任何遗漏。

此例之中被移函数只取用(指涉)一个值域,所以我只需将这个值域作为参数传给target method就行了。如果被移函数调用了Account中的另一个函数,我就不能这么简单地处理。这种情况下我必须将source object传递给target method:
class AccountType...
    double overdraftCharge(Account account) {
       if(isPremium()) {
          double result = 10;
          if(account.getDaysOverdrawn() >7)
             result += (account.getdaysOverdrawn() - 7) * 0.85;
          return result;
        }
       else return daysOverdrawn * 1.75;
    }
如果我需要source class的多个特性,那么我也会将source object传递给target method。不过如果target method需要太多source class特性,就得进一步重构。通常这种情况下我会分解target method,并将其中一部分移回source class。
posted @ 2005-08-30 11:08 ivaneeo 阅读(221) | 评论 (0)编辑 收藏

作法(Mechanics)
    • 检查source class定义之source method所使用的一切特性(features),考虑它们是否也该被搬移。
        • ==》如果某个特性只被你打算搬移的那个函数用到,你应该将它一并搬移。如果另有其他函数使用了这个特性,你可以考虑将使用该特性的所有函数全部一并搬移。有时侯搬移一组函数比逐一搬移简单些。
    • 检查source class的subclass和superclass,看看是否有该函数的其他声明。
        • ==》如果出现其他声明,你或许无法进行搬移,除非target class也同样表现出多态性(polymorphism)。
    • 在target class中声明这个函数。
        • ==》你可以为此函数选择一个新名称 -- 对target class更有意义的名称。
    • 将source method的代码拷贝到target method中。调整后者,使其能在新家中正常运行。
        • ==》如果target method使用了source特性,你得决定如何从target method引用source object。如果target class中没有相应的引用机制,就把source object reference当作参数,转给新建立的target method。
        • ==》如果source method包含异常处理式(exception handler),你得判断逻辑上应该由哪个class来处理这一异常。如果应该由source class来负责,就把异常处理式留在原地。
    • 编译target class。
    • 决定如何从source正确引用target object。
        • ==》可能会有一个现成的值域或函数帮助你取得target object。如果没有,就看能否轻松建立一个这样的函数。如果还是不行,你得在source class中新建一个新值域来保存target object。这可能是一个永久性修改,但你也可以让它保持暂时的地位,因为后继的其他重构项目可能会把这个新建值域去掉。
    • 修改source method,使之成为一个delegating method(纯委托函数)。
    • 编译,测试。
    • 决定[删除source method]或将它当作一个delegating method保留下来。
        • ==》如果你经常要在source object中引用target method,那么将source method作为delegating method保留下来会比较简单。
    • 如果你移除source method,请将source class中对source method的所有引用动作,替换为[对target method的引用动作]。
        • ==》你可以每修改一个引用点就编译并测试一次。也可以通过一次[查找/替换]改掉所有引用点,这通常简单一些。
    • 编译,测试。
posted @ 2005-08-30 10:51 ivaneeo 阅读(155) | 评论 (0)编辑 收藏

动机(Motivation)
[函数搬移]是重构理论的支柱。如果一个class有太多行为,或如果一个class与另一个class有太多合作而形成高度耦合(highly coupled),我就会搬移函数。通过这种手段,我可以使系统中的classes更简单,这些classes最终也将更干净利落地实现系统交付的任务。

常常我会浏览class的所有函数,从中寻找这样的函数:使用另一个对象的次数比使用自己所驻对象的次数还多。一旦我移动了一些值域,就该做这样的检查。 一旦发现[有可能被我搬移]的函数,我就会观察调用它的那一端、它调用的那一端,以及继承体系中它的任何一个重定义函数。然后,我会根据[这个函数与哪个 对象的交流比较多],决定其移动路径。

这往往不是一个容易做出的决定。如果不能肯定是否应该移动一个函数,我就会继续观察其他函数。移动其他函数往往会让这项决定变得容易一些。有时候,即使你 移动了其他函数,还是很难对眼下这个函数做出决定。其实这也没什么大不了的。如果真的很难做出决定,那么或许[移动这个函数与否]并不那么重要。所以,我 会凭本能去做,反正以后总是可以修改的。
posted @ 2005-08-30 10:15 ivaneeo 阅读(158) | 评论 (0)编辑 收藏

你的程序中,有个函数与其所驻class之外的另一个class进行更多交流:调用后者,或被后者调用。

在该函数最常引用(指涉)的class中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数(delegating method),或者将旧函数完全移除。

Move_Method.png
posted @ 2005-08-30 09:16 ivaneeo 阅读(158) | 评论 (0)编辑 收藏

作法(Mechanics)
    • 准备好你的另一个(替换用)算法,让它通过编译。
    • 针对现有测试,执行上述的新算法。如果结果于原本结果相同,重构结束。
    • 如果测试结果不同于原先,在测试和调试过程中,以旧算法为比较参照标准。
          • ==》对于每个test case(测试用例),分别以新旧两种算法执行,并观察两者结果是否相同。这可以帮助你看到哪一个test case出现麻烦,以及出现了怎么的麻烦。
posted @ 2005-08-29 17:25 ivaneeo 阅读(218) | 评论 (0)编辑 收藏

动机(Motivation)
有时侯你会想要修改原先的算法,让它去做一件与原先动作略有差异的事。这时候你也可以先把原先的算法替换为一个较易修改的算法,这样后续的修改会轻松许多。

使用这项重构手法之前,请先确定自己已经尽可能分解了原先函数。替换一个巨大而复杂的算法是非常困难的,只有先将它分解为较简单的小型函数,然后你才能很有把握地进行算法替换工作。
posted @ 2005-08-29 17:20 ivaneeo 阅读(165) | 评论 (0)编辑 收藏

仅列出标题
共67页: First 上一页 45 46 47 48 49 50 51 52 53 下一页 Last