ivaneeo's blog

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

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

#

现在我运用同样手法处理getFrequentRenterPoints()。重构前的样子如下:
class Movie...
    int getFrequentRenterPoints(int daysRented) {
       if((getPriceCode() == Movie.NEW_RELEASE) && daysRented > 1)
          return 2;
       else
          return 1;
    }

首先我把这个函数移到Price class里头:
class Movie...
    int getFrequentRenterPoints(int daysRented) {
       return _price.getFrequentPoints(daysRented);
    }
class Price...
    int getFrequentRenterPoints(int daysRented) {
       if((getPriceCode() == Movie.NEW_RELEASE) && daysRented > 1)
          return 2;
       else
          return 1;
    }

但是这一次我不把superclass函数声明为abstract。我只是为[新片类型]产生一个覆写函数(override method),并在superclass内留下一个已定义的函数,使它成为一种缺省行为。

class NewReleasePrice
    int getFrequentRenterPoints(int daysRented) {
       return (daysRented > 1) ? 2 : 1;
    }

class Price...
    int getFrequentRenterPoints(int daysRented) {
       return 1;
    }
posted @ 2005-08-16 15:50 ivaneeo 阅读(155) | 评论 (0)编辑 收藏

现在我要对getCharge()实施Move Method(142).下面是重构前的代码:
class Movie...
double getCharge(int daysRented) {
    double result = 0;
    switch(getPriceCode()) {   //取得影片出租价格
          case Movie.REGULAR:   //普通片
             result+= 2;
             if(getDaysRented() > 2)
                result+= (getDaysRented() - 2) * 1.5;
             break;
          case Movie.NEW_RELEASE:   //新片
             result+= getDaysRented() * 3;
             break;
          case Movie.CHILDRENS:   //儿童片
             result+= 1.5;
             if(getDaysRented() > 3)
                result+= (getDaysRented() - 3) * 1.5;
             break;
       }
    return result;
}

搬移动作很简单。下面是重构后的代码:
class Movie...
    double getCharge(int daysRented) {
       return _price.getCharge(daysRented);
    }


class Price...
   double getCharge(int daysRented) {
    double result = 0;
    switch(getPriceCode()) {   //取得影片出租价格
          case Movie.REGULAR:   //普通片
             result+= 2;
             if(getDaysRented() > 2)
                result+= (getDaysRented() - 2) * 1.5;
             break;
          case Movie.NEW_RELEASE:   //新片
             result+= getDaysRented() * 3;
             break;
          case Movie.CHILDRENS:   //儿童片
             result+= 1.5;
             if(getDaysRented() > 3)
                result+= (getDaysRented() - 3) * 1.5;
             break;
       }
    return result;
}

搬移之后,我就可以开始运用Replace Conditional with Polymorphism(255)了。
下面是重构前的代码:
class Price...
   double getCharge(int daysRented) {
    double result = 0;
    switch(getPriceCode()) {   //取得影片出租价格
          case Movie.REGULAR:   //普通片
             result+= 2;
             if(getDaysRented() > 2)
                result+= (getDaysRented() - 2) * 1.5;
             break;
          case Movie.NEW_RELEASE:   //新片
             result+= getDaysRented() * 3;
             break;
          case Movie.CHILDRENS:   //儿童片
             result+= 1.5;
             if(getDaysRented() > 3)
                result+= (getDaysRented() - 3) * 1.5;
             break;
       }
    return result;
}

我的作法是一次取出一个case分支,在相应的class内建一个覆写函数(override method)。先从RegularPrice开始:
class RegularPrice...
    double getCharge(int daysRented) {
       double result = 2;
       if(daysRented > 2)
          result += (daysRented - 2) * 1.5;
       return result;
    }

class ChildernsPrice...
    double getCharge(int daysRented) {
       double result = 1.5;
       if(daysRented > 3)
          result += (daysRented - 3) * 1.5;
       return result;
    }

class NewReleasePrice...
double getCharge(int daysRented) {
       return daysRented * 3;
    }

处理完所有case分支之后,我就把Price.getCharge()声明为abstract:
class Price...
    abstract double getCharge(int daysRented);
posted @ 2005-08-15 17:21 ivaneeo 阅读(164) | 评论 (0)编辑 收藏

首先我要使用Replace Type Code with State/Strategy(227).第一步骤是针对[与型别相依的行为]使用Self Encapsulate Field(171),确保任何时候都通过gettingsetting两个函数来运用这些行为。由于多数代码来自其他classes,所以多数函数都已经使用getting函数。但构造函数(constructor)仍然直接访问价格代码:
class Movie...
    public Movie(String name, int priceCode) {
       _title = name;
       _priceCode = priceCode;
    }

我可以用一个setting函数来代替:
class Movie...
    public Movie(String name, int priceCode) {
       _title = name;
       setPriceCode(priceCode);
    }
现在我加入新class,并在price对象中提供[与型别相依的行为]。为了实现这一点,我在Price内加入一个抽象函数(abstract method),并在其所有subclasses中加上对应的具体函数(concrete method):

abstract class Price {
    abstract int getPriceCode();   //取得价格代号
}
class ChildernsPrice extends Price {
    int getPriceCode() {
       return Movie.CHILDERNS;
    }
}
class NewReleasePrice extends Price {
    int getPriceCode() {
       return Movie.NEW_RELEASE;
    }
}
class RegularPrice extends Price {
    int getPriceCode() {
       return Movie.REGULAR;
    }
}

现在,我需要修改Movie class内的[价格代号]访问函数(get/set函数,如下),让它们使用新class。下面是重构前的样子:
public int getPriceCode() {
    return _priceCode;
}
public void setPriceCode(int arg) {
    _priceCode;
}
private int _priceCode;

这个意味我必须在Movie class内保存一个price对象,而不再是保存一个_priceCode变量。此外我还需要修改访问函数:
class Movie...
    public int getPriceCode() {   //取得价格代号
       return _price.getPriceCode();
    }
    public void setPriceCode(int arg) {   //设定价格代码
       switch(arg) {
          case REGULAR:   //普通片
             _price = new RegularPrice();
             break;
          case CHILDERNS:   //儿童片
             _price = new ChildernsPrice();
             break;
          case NEW_RELEASE:   //新片
             _price = new NewReleasePrice();
             break;
          default:
             throw new IllegalArument Exception("Incorrect Price Code");
       }
    }
    private Price _price;
posted @ 2005-08-15 17:18 ivaneeo 阅读(170) | 评论 (0)编辑 收藏

终于。。。。。我们来到继承(inheritance)
我们有数种影片类型,它们以不同的方式回答相同的问题。这听起来很像subclasses的工作。我们可以建立Movie的三个subclasses,每个都有自己的计费法。

这么一来我就可以运用多态(polymorphism)来取代switch语句了。很遗憾的是这里有个小问题,不能这么干。一部影片可以在生命期周期内修 改自己的分类,一个对象却不能在生命周期内修改自己的分类,一个对象却不能在生命周期内修改自己所属的class。不过还是有一个解决方法:state pattern(模式)。加入这一层间接性,我们就可以在Price对象内进行subclassing动作,于是便可在任何必要时刻修改价格。

为了引入state模式,我使用三个重构准则。首先运用Replace Type Code with State/Strategy(227),将[与型相依的行为](type code behavior)搬移至state模式内。然后运用Move Method(142)将switch语句移到Price class里头。最后运用Replace Conditional with Polymorphism(255)去掉switch语句。
posted @ 2005-08-15 16:16 ivaneeo 阅读(169) | 评论 (0)编辑 收藏

运用多态(polymorphism)取代与价格相关的条件逻辑
这个问题的第一部分是switch语句。在另一个对象的属性(attribute)基础上运用switch语句,并不是什么好注意。如果不得不使用,也应该在对象自己的数据上使用,而不是在别人的数据上使用。
class Rental...
double getCharge() {
    double result = 0;
    switch(getMovie().getPriceCode()) {   //取得影片出租价格
          case Movie.REGULAR:   //普通片
             result+= 2;
             if(getDaysRented() > 2)
                result+= (getDaysRented() - 2) * 1.5;
             break;
          case Movie.NEW_RELEASE:   //新片
             result+= getDaysRented() * 3;
             break;
          case Movie.CHILDRENS:   //儿童片
             result+= 1.5;
             if(getDaysRented() > 3)
                result+= (getDaysRented() - 3) * 1.5;
             break;
       }
    return result;
}

这暗示getCharge()应该移到Movie class里头去:
class Movie...
double getCharge(int daysRented) {
    double result = 0;
    switch(getPriceCode()) {   //取得影片出租价格
          case Movie.REGULAR:   //普通片
             result+= 2;
             if(getDaysRented() > 2)
                result+= (getDaysRented() - 2) * 1.5;
             break;
          case Movie.NEW_RELEASE:   //新片
             result+= getDaysRented() * 3;
             break;
          case Movie.CHILDRENS:   //儿童片
             result+= 1.5;
             if(getDaysRented() > 3)
                result+= (getDaysRented() - 3) * 1.5;
             break;
       }
    return result;
}

为了让它得以运作,我必须把[租期长度]作为参数传递进去。当然,[租期长度]来自 Rental对象.计算费用时需要两份数据:[租期长度]和[影片类型]。为什么我选择[将租期长度传给Movie对象]而不是[将影片类型传给 Rental对象]呢?因为本系统可能发生的变化是加入新影片类型,这种变化带有不稳定倾向。如果影片类型有所变化,我希望掀起最小的涟漪,所以我选择在 Movie对象内计算费用。

我把上述计费方法放进Movie class里头,然后修改Rental的getCharge(),让它使用这个新函数:
class Rental...
    double getCharge() {
       return _movie.getCharge(_daysRented);
    }
posted @ 2005-08-15 14:54 ivaneeo 阅读(182) | 评论 (0)编辑 收藏

很明显看出来,htmlStatement()和statement()是不同的。现在,我应该脱下[重构]的帽子,戴上[添加功能]的帽子,戴上[添加功能]的帽子。我可以像下面这样编写htmlStatement(),并添加相应测试:

public String htmlStatement() {
    Enumeration rentals = _rentals.elements();
    String result = "<H1>Rental Record for <EM> " + getName() + "</EM></H1><P>\n";
    while(rentals.hasMoreElements()) {
       Rental each = (Rental)rentals.nextElement();    //取得一笔租借记录

          result += each.getMovie().getTitle() + ":" +
             String.valueOf(each.getCharge()) + "<BR>\n";
    }
//   add footer lines(结尾打印)
    result += "<P>You owe <EM>" + String.valueOf(getTotalCharge()) + "</EM><P> \n";
    result += "On this rental you earned <EM>" + String.valueOf(getTotalfrequentRenterPoints()) +
       "</EM>frequent renter points<P>";
    return result;
}

通过计算逻辑的提炼,我可以完成一个htmlStatement(),并复用(reuse)原本statement()内的所有计算。我不必剪剪贴贴,所以如果计算规则发生改变,我只需在程序中做一处修改。
posted @ 2005-08-15 14:29 ivaneeo 阅读(161) | 评论 (0)编辑 收藏

然后以同样手法处理frequentRenterPoints:
class Customer...
public String statement() {
    int frequentRenterPoints = 0;
    Enumeration rentals = _rentals.elements();
    String result = "Rental Record for * " + getName() + "\n";
    while(rentals.hasMoreElements()) {
       Rental each = (Rental)rentals.nextElement();    //取得一笔租借记录

     frequentRenterPoints += each.getFrequentRenterPoints();

          result += "\t" + each.getMovie().getTitle() + "\t" +
             String.valueOf(each.getCharge()) + "\n";
    }
//   add footer lines(结尾打印)
    result += "Amount owed is " + String.valueOf(getTotalCharge()) + " \n";
    result += "You earned " + String.valueOf(frequentRenterPoints) +
       "frequent renter points";
    return result;
}

--------------------------------------------------------------------------------------------------------------------

public String statement() {
    Enumeration rentals = _rentals.elements();
    String result = "Rental Record for * " + getName() + "\n";
    while(rentals.hasMoreElements()) {
       Rental each = (Rental)rentals.nextElement();    //取得一笔租借记录

          result += "\t" + each.getMovie().getTitle() + "\t" +
             String.valueOf(each.getCharge()) + "\n";
    }
//   add footer lines(结尾打印)
    result += "Amount owed is " + String.valueOf(getTotalCharge()) + " \n";
    result += "You earned " + String.valueOf(getTotalfrequentRenterPoints()) +
       "frequent renter points";
    return result;
}

// 译注:此即所谓query method
private int getTotalFrequentRenterPoints() {
    int result = 0;
    Enumeration rentals = _rentals.elements();
    while(rentals.hasMoreElements()) {
         Rental each = (Rental)rentals.nextElement();
         result += each.getFrequentRenterPoints();
    }
    return result;
}
posted @ 2005-08-15 14:10 ivaneeo 阅读(169) | 评论 (0)编辑 收藏

正如我在前面提过的,临时变量可能是个问题。它们只在自己所属的函数中有效,所以它们会助长[冗长而复杂]的函数。这里我们有两个临时变量,两者都是用来 从Customer对象相关的Rental对象中获得某个总量。不论ASCII版或HTML版都需要这些总量。我打算运用Replace Temp with Query(120),并利用所谓的query method来取代totalAmount或frequentRentalPoints这两个临时变量。由于class内的任何函数都可以取用(调用)上述所谓query methods,所以它可能够促进较干净的设计,而非冗长复杂的函数:

class Customer...
public String statement() {
    double totalAmount = 0;
    int frequentRenterPoints = 0;
    Enumeration rentals = _rentals.elements();
    String result = "Rental Record for * " + getName() + "\n";
    while(rentals.hasMoreElements()) {
       Rental each = (Rental)rentals.nextElement();    //取得一笔租借记录

     frequentRenterPointers += each.getFrequentRenterPoints();

          result += "\t" + each.getMovie().getTitle() + "\t" +
             String.valueOf(each.getCharge()) + "\n";
          totalAmount += each.getCharge();
    }
//   add footer lines(结尾打印)
    result += "Amount owed is " + String.valueOf(totalAmount) + " \n";
    result += "You earned " + String.valueOf(frequentRenterPoints) +
       "frequent renter points";
    return result;
}

首先我以Customer class的getTotalCharge()取代totalAmount:
class Customer...
public String statement() {
    int frequentRenterPoints = 0;
    Enumeration rentals = _rentals.elements();
    String result = "Rental Record for * " + getName() + "\n";
    while(rentals.hasMoreElements()) {
       Rental each = (Rental)rentals.nextElement();    //取得一笔租借记录

     frequentRenterPointers += each.getFrequentRenterPoints();

          result += "\t" + each.getMovie().getTitle() + "\t" +
             String.valueOf(each.getCharge()) + "\n";
    }
//   add footer lines(结尾打印)
    result += "Amount owed is " + String.valueOf(getTotalCharge()) + " \n";
    result += "You earned " + String.valueOf(frequentRenterPoints) +
       "frequent renter points";
    return result;
}

//  译注:次即query method
private double getTotalCharge() {
    double result = 0;
    Enumeration rentals = _rentals.elements();
    while(rentals.hasMoreElements()) {
       Rental each = (Rental)rentals.nextElement();
       result += each.getCharge();
    }
    return result;
}
这并不是Replace Temp with Query(120)的最简单情况。由于totalAmount在循环内部被赋值,我不得不把循环复制到query method中
posted @ 2005-08-15 13:55 ivaneeo 阅读(221) | 评论 (0)编辑 收藏

提取[常客积点计算]代码
首先我们需要针对[常客积点计算]这部分代码(以下粗体部分)运用Extract Method(110)重构准则:
public String statement() {
    double totalAmount = 0;
    int frequentRenterPoints = 0;
    Enumeration rentals = _rentals.elements();
    String result = "Rental Record for * " + getName() + "\n";
    while(rentals.hasMoreElements()) {
       Rental each = (Rental)rentals.nextElement();    //取得一笔租借记录

       //   add frequent renter points(累加常客积点)
          frequentRenterPoints ++;
          if((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) &&
             each.getDaysRented() > 1)
             frequentRenterPoints ++;

          result += "\t" + each.getMovie().getTitle() + "\t" +
             String.valueOf(each.getCharge()) + "\n";
          totalAmount += each.getCharge();
    }
//   add footer lines(结尾打印)
    result += "Amount owed is " + String.valueOf(totalAmount) + " \n";
    result += "You earned " + String.valueOf(frequentRenterPoints) +
       "frequent renter points";
    return result;
}

再一次我又要寻找局部变量。这里再一次用到了each,而它可以被当作参数传入新函数中。另一个临时变量是frequentRenterPoints。本 例中的它在被使用之前已经先有初值,但提炼出来的函数并没有读取改值,所以我们不需要将它当作参数传进去,只需对它执行[付添赋值操作](appending assignment,operator+=)就行了。

public String statement() {
    double totalAmount = 0;
    int frequentRenterPoints = 0;
    Enumeration rentals = _rentals.elements();
    String result = "Rental Record for * " + getName() + "\n";
    while(rentals.hasMoreElements()) {
       Rental each = (Rental)rentals.nextElement();    //取得一笔租借记录

     frequentRenterPointers += each.getFrequentRenterPoints();

          result += "\t" + each.getMovie().getTitle() + "\t" +
             String.valueOf(each.getCharge()) + "\n";
          totalAmount += each.getCharge();
    }
//   add footer lines(结尾打印)
    result += "Amount owed is " + String.valueOf(totalAmount) + " \n";
    result += "You earned " + String.valueOf(frequentRenterPoints) +
       "frequent renter points";
    return result;
}

class Rental...
    int getFrequentRenterPoints() {
       if((getMovie().getPriceCode() == Movie.NEW_RELEASE)
          && getDaysRented() > 1)
          return 2;
       else
          return 1;
    }
posted @ 2005-08-15 13:29 ivaneeo 阅读(267) | 评论 (0)编辑 收藏

Customer.statement():
public String statement() {
    double totalAmount = 0;
    int frequentRenterPoints = 0;
    Enumeration rentals = _rentals.elements();
    String result = "Rental Record for * " + getName() + "\n";
    while(rentals.hasMoreElements()) {
       double thisAmount = 0;
       Rental each = (Rental)rentals.nextElement();    //取得一笔租借记录

       thisAmount = each.getCharge();

       //   add frequent renter points(累加常客积点)
          frequentRenterPoints ++;
          if((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) &&
             each.getDaysRented() > 1)
             frequentRenterPoints ++;

          result += "\t" + each.getMovie().getTitle() + "\t" +
             String.valueOf(thisAmount) + "\n";
          totalAmount += thisAmount;
    }
//   add footer lines(结尾打印)
    result += "Amount owed is " + String.valueOf(totalAmount) + " \n";
    result += "You earned " + String.valueOf(frequentRenterPoints) +
       "frequent renter points";
    return result;
}

下一件引我注意的事时:thisAmount如今变成多余了.它接受each.getCharge()的执行结果,然后就不再有任何改变.所以我可以运用Replace Temp with Query(120)把thisAmount除去:
public String statement() {
    double totalAmount = 0;
    int frequentRenterPoints = 0;
    Enumeration rentals = _rentals.elements();
    String result = "Rental Record for * " + getName() + "\n";
    while(rentals.hasMoreElements()) {
       Rental each = (Rental)rentals.nextElement();    //取得一笔租借记录

       //   add frequent renter points(累加常客积点)
          frequentRenterPoints ++;
          if((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) &&
             each.getDaysRented() > 1)
             frequentRenterPoints ++;

          result += "\t" + each.getMovie().getTitle() + "\t" +
             String.valueOf(each.getCharge()) + "\n";
          totalAmount += each.getCharge();
    }
//   add footer lines(结尾打印)
    result += "Amount owed is " + String.valueOf(totalAmount) + " \n";
    result += "You earned " + String.valueOf(frequentRenterPoints) +
       "frequent renter points";
    return result;
}
我喜欢尽量除去这一类临时变量.临时变量往往形成问题.它们会导致大量参数被传来传 去,而其实完全没有这种必要.你很容易失去它们的踪迹,尤其在长长的函数之中更是如此.当然我这么做也需付出性能上的代价,例如本例的费用就被计算了两 次.但是这很容易在Rental class中被优化.而且如果代码有合理的组织和管理,优化会有很好的效果.
posted @ 2005-08-15 13:12 ivaneeo 阅读(188) | 评论 (0)编辑 收藏

仅列出标题
共67页: First 上一页 53 54 55 56 57 58 59 60 61 下一页 Last