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 閱讀(159) | 評論 (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 閱讀(172) | 評論 (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 閱讀(177) | 評論 (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 閱讀(176) | 評論 (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 閱讀(187) | 評論 (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 閱讀(166) | 評論 (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 閱讀(175) | 評論 (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 閱讀(227) | 評論 (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 閱讀(276) | 評論 (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 閱讀(193) | 評論 (0)編輯 收藏

          僅列出標題
          共67頁: First 上一頁 53 54 55 56 57 58 59 60 61 下一頁 Last 
          主站蜘蛛池模板: 天台县| 普定县| 抚顺县| 江永县| 浙江省| 资中县| 广河县| 将乐县| 丹巴县| 凭祥市| 双流县| 镇巴县| 定兴县| 二手房| 腾冲县| 雅安市| 申扎县| 博兴县| 苏尼特右旗| 闵行区| 涿鹿县| 富民县| 汶上县| 馆陶县| 溆浦县| 徐水县| 海阳市| 台南县| 临湘市| 灵武市| 怀远县| 神木县| 英超| 石台县| 贵港市| 湛江市| 江川县| 乌恰县| 中超| 班戈县| 合水县|