ivaneeo's blog

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

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

#

你想要把某个算法替换为另一个更清晰的算法。

将函数本体(method body)替换为另一个算法。

String foundPerson(String[] people) {
    for(int i = 0; i < people.length; i++) {
       if(people[i].equals("Don")) {
          return "Don";
        }
       if(people[i].equals("John")) {
          return "John";
        }
       if(people[i].equals("Kent")) {
          return "Kent";
        }
    }
    return "";
}

                             |  |
                            \   /
String foundPerson(String[] people) {
    List candidates = Arrays.asList(new String[],
                                                       {"Don", "John", "Kent"});
    for(int i = 0; i < people.length; i++)
       if(candidates.contains(people[i]))
          return people[i];
    return "";
}
posted @ 2005-08-29 17:03 ivaneeo 阅读(199) | 评论 (0)编辑 收藏

范例(Examples)
class Account...
    int gamma(int inputVal, int quantity, int yearToDate) {
       int importantValue1 = (inputVal * quantity) + delta();
       int importantValue2 = (inputVal * yearToDate) + 100;
       if((yearToDate - importantValue1) > 100)
          importantValue2 -= 20;
       int importantValue3 = importantValue2 * 7;
       //   and so on.
       return importantValue3 -2 * importantValue1;
    }

为了把这个函数变成一个函数对象(method object),我首先需要声明一个新class。在此新class中我应该提供一个final值域用以保存原先对象(源对象):对于函数的每一个参数和每一个临时变量,也以一个个值域逐一保存。
class Gamma...
    private final Account _account;
    private int inputVal;
    private int quantity;
    private int yearToDate;
    private int importantValue1;
    private int importantValue2;
    private int importantValue3;

接下来,加入一个构造函数:

Gamma (Account source, int inputValArg, int quantityArg, int yearToDateArg) {
    _account = source;
    inputVal = inputValArg;
    quantity = quantityArg;
    yearToDate = yearToDateArg;
}

现在可以把原来的函数搬到compute()了。函数中任何调用Account class的地方,我都必须改而使用_account值域:
int compute() {
       int importantValue1 = (inputVal * quantity) + _account.delta();
       int importantValue2 = (inputVal * yearToDate) + 100;
       if((yearToDate - importantValue1) > 100)
          importantValue2 -= 20;
       int importantValue3 = importantValue2 * 7;
       //   and so on.
       return importantValue3 -2 * importantValue1;
}
然后,我修改旧函数,让它将它的工作转发给刚完成的这个函数对象(method object):
int gamma(int inputVal, int quantity, int yearToDate) {
    return new Gamma(this, inputVal, quantity, yearToDate).compute();
}
这就是本项重构的基本原则。它带来的好处是:现在我可以轻松地对compute()函数采取Extract Method(110),不必担心引数(argument)传递。
int compute() {
    int importantValue1 = (inputVal * quantity) + _account.delta();
       int importantValue2 = (inputVal * yearToDate) + 100;
       importantThing();
       int importantValue3 = importantValue2 * 7;
       //   and so on.
       return importantValue3 -2 * importantValue1;
}
void importantThing() {
    if((yearToDate - importantValue1) > 100)
       importantValue2 -= 20;
}
posted @ 2005-08-29 16:49 ivaneeo 阅读(255) | 评论 (0)编辑 收藏

作法(Mechanics)
    • 建立一个新class,根据[待被处理之函数]的用途,为这个class命名。
    • 在新class中建立一个final值域,用以保存原先大型函数所驻对象。我们将这个值域称为[源对象]。同时,针对原(旧)函数的每个临时变量和每个参数,在新class中建立一个个对应的值域保存之。
    • 在新class中建立一个构造函数(constructor),接收源对象及原函数的所有参数作为参数。
    • 在新class中建立一个compute()函数。
    • 将原(旧)函数的代码拷贝到compute()函数中。如果需要调用源对象的任何函数,请以[源对象]值域调用。
    • 编译。
    • 将旧函数的函数本体替换为这样一条语句:[创建上述新class的一个新对象,而后调用其中的compute()函数]。
现在进行到很有趣的部分了。由于所有局部变量现在都成了值域,所以你可以任意分解这个大型函数,不必传递任何参数。
posted @ 2005-08-29 16:13 ivaneeo 阅读(143) | 评论 (0)编辑 收藏

动机(Motivation)
我在本书中不断向读者强调小型函数的优美动人。只要将相对独立的代码从大型函数中提炼出来,就可以大大提高代码的可读性。

但是,局部变量的存在会增加函数分解难度。如果一个函数之中局部变量泛滥成灾,那么想分解这个函数是非常困难的。Replace Temp with Query(120)可以助你减轻这一负担,但有时候你会发现根本无法拆解的函数。这种情况下,你应该把手深深地伸入你的工具箱(好酒沉瓮底呢),祭出函数对象(method object)这件法宝。

Replace Method with Method Object(135)会将所有局部变量都变成函数对象(method object)的值域(field)。然后你就可以对这个新对象使用Extract Method(110)创造出新函数,从而将原本的大型函数拆解变短。
posted @ 2005-08-29 15:56 ivaneeo 阅读(172) | 评论 (0)编辑 收藏

你有一个大型函数,其中对局部变量的使用,使你采用Extract Method(110)。

将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的值域(field)。然后你可以在同一个对象中将这个大型函数分解为数个小型函数。

class Order...
    double price() {
       double primaryBasePrice;
       double secondaryBasePrice;
       double tertiaryBasePrice;
       //   long computation;
      ...
    }
posted @ 2005-08-29 15:47 ivaneeo 阅读(142) | 评论 (0)编辑 收藏

范例(Examples)
我从下列这段简单代码开始:
int discount(int inputVal, int quantity, int yearToDate) {
    if(inputVal > 50) inputVal -= 2;
    if(quantity > 100) inputVal -= 1;
    if(yearToDate > 1000) inputVal -= 4;
    return inputVal;
}
以临时变量取代对参数的赋值动作,得到下列代码:
 int discount(int inputVal, int quantity, int yearToDate) {
    int result = inputVal;
    if(inputVal > 50)
result -= 2;
    if(quantity > 100) result -= 1;
    if(yearToDate > 1000) result -= 4;
    return result
}
还可以为参数加上关键词final,从而强制它遵循[不对参数赋值]这一惯例:
  int discount(final int inputVal, final int quantity, final int yearToDate) {
    int result = inputVal;
    if(inputVal > 50)
result -= 2;
    if(quantity > 100) result -= 1;
    if(yearToDate > 1000) result -= 4;
    return result
}

不过我的承认,我并不经常使用final来修饰参数,因为我发现,对于提高短函数的清晰度,这个办法并无太大帮助。我通常会在较长的函数中使用它,让它帮助我检查参数是否被做了修改。
posted @ 2005-08-29 15:40 ivaneeo 阅读(203) | 评论 (0)编辑 收藏

作法(Mechanics)
    • 建立一个临时变量,把待处理的参数值赋予它。
    • 以[对参数的赋值动作]为界,将其后所有对此参数的引用点,全部替换为[对此临时变量的引用动作]。
    • 修改赋值语句,使其改为对新建之临时变量赋值。
    • 编译,测试。
          • =》如果代码的语义是pass by reference,请在调用端检查调用后是否还使用了这个参数。也要检查有多少个pass by reference参数[被赋值后又被使用]。请尽量只以return方式返回一个值。如果需要返回的值不只一个,看看可否把需返回的大堆数据变成单一对 象,或干脆为每个返回值设计对应的一个独立函数。
posted @ 2005-08-29 15:26 ivaneeo 阅读(180) | 评论 (0)编辑 收藏

动机(Motivation)

我只针对[foo被改而指向(引用)完全不同的另一个对象]这种情况来讨论:
    void aMethod(Object foo) {
       foo.modifyInSomeWay();   //that's OK
       foo = anotherObject;   //throuble and despair will follow you
    }
我之所以不喜欢这样的作法,因为它降低了代码的清晰度,而且混淆了pass by value(传值)和pass by reference(传址)这两种参数传递方式。Java只采用pass by value传递方式,我们的讨论也正是基于这一点。

在pass by value情况下,对参数的任何修改,都不会调用端造成任何影响。那些用过pass by reference的人可能会在这一点上犯糊涂。
posted @ 2005-08-29 15:16 ivaneeo 阅读(172) | 评论 (0)编辑 收藏

你的代码对一个参数进行赋值动作。

以一个临时变量取代该参数的位置。

int discount(int inputVal, int quantity, int yearToDate) {
    if(inputVal > 50) inputVal -= 2;
                                                       |  |
                                    \  /
int discount(int inputVal, int quantity, int yearToDate) {
    int result = inputVal;
    if(inputVal > 50) result -= 2;
posted @ 2005-08-29 15:00 ivaneeo 阅读(237) | 评论 (0)编辑 收藏

范例(Examples)
下面范例中我要计算一个苏格兰布丁(haggis)运动的距离。在起点处,静止的苏格兰布丁会受到一个初始力的作用而开始运动。一段时机后,第二个力作用于布丁,让它再次加速。根据牛顿第二定律,我可以这样计算布丁运动的距离:

double getDistanceTravelled(int time) {
    double result;
    double acc = _primaryForce / _mass;
    int primaryTime = Math.min(time, _delay);
    result = 0.5 * acc * primaryTime * primaryTime;
    int secondaryTime = time - _delay;
    if(secondaryTime > 0) {
       double primaryVel = acc * _delay;
       acc = (_primaryForce + _secondaryForce) / _mass;
       result += primaryvel * secondaryTime + 0.5 * acc * secondaryTime * secondaryTime;
    }
    return result;
}

acc变量有两个责任:第一是保存第一个力造成的初始加速度;第二是保存两个力共同造成的加速度。这就是我想要剖解的东西。

首先,我在函数开始处修改这个临时变量的名称,并将新的临时变量声明为final。接下来我把第二次赋值之前对acc变量的所有引用点,全部改用新的临时变量。最后,我在第二次赋值处重新声明acc变量:

double getDistanceTravelled(int time) {
    double result;
    final double primaryAcc = _primaryForce / _mass;
    int primaryTime = Math.min(time, _delay);
    result = 0.5 * primaryAcc * primaryTime * primaryTime;
    int secondaryTime = time - _delay;
    if(secondaryTime > 0) {
       double primaryVel = primaryAcc * _delay;
       double acc = (_primaryForce + _secondaryForce) / _mass;
       result += primaryvel * secondaryTime + 0.5 * acc * secondaryTime * secondaryTime;
    }
    return result;
}

新的临时变量的名称指出,它只承担原先acc变量的第一责任。我将它声明为final,确保它只被赋值一次。然后,我在原先acc变量第二次被赋值处重新声明acc。现在,重新编译并测试,一切都应该没有问题。

然后,我继续处理acc临时变量的第二次赋值。这次我把原先的临时变量完全删掉,代之以一个新的临时变量。新变量的名称指出,它只承担原先acc变量的第二个责任:

double getDistanceTravelled(int time) {
    double result;
    final double primaryAcc = _primaryForce / _mass;
    int primaryTime = Math.min(time, _delay);
    result = 0.5 * primaryAcc * primaryTime * primaryTime;
    int secondaryTime = time - _delay;
    if(secondaryTime > 0) {
       double primaryVel = primaryAcc * _delay;
       final double secondaryAcc = (_primaryForce + _secondaryForce) / _mass;
       result += primaryvel * secondaryTime + 0.5 * secondaryAcc * secondaryTime * secondaryTime;
    }
    return result;
}
posted @ 2005-08-29 14:56 ivaneeo 阅读(182) | 评论 (0)编辑 收藏

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