<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ### 運用多態(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 (daysRented > 2) result += (daysRented - 2) * 1.5; break; case Movie.NEW_RELEASE: result += daysRented * 3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented > 3) result += (daysRented - 3) * 1.5; break; } return result; } ~~~ 為了讓它得以運作,我必須把「租期長度」作為參數傳遞進去。當然,「租期長度」來自收Rental 對象。計算費用時需要兩份數據:「租期長度」和「影片類型」。為什么我選擇「將租期長度傳給Movie 對象」而不是「將影片類型傳給Rental 對象」呢?因為本系統可能發生的變化是加入新影片類型,這種變化帶有不穩定傾向。如果影片類型有所變化,我希望掀起最小的鏈滴,所以我選擇在Movie 對象內計算費用。 我把上述計費方法放進Movie class 里頭,然后修改Rental 的getCharge(),讓它使用這個新函數(圖1.12和圖1.13): ![](https://box.kancloud.cn/2016-08-15_57b1b52e67dc4.gif) 圖1.12 本節所討論的兩個函數被移到Movie class 內之前系統的class diagram ![](https://box.kancloud.cn/2016-08-15_57b1b52e800f0.gif) 圖1.13 本節所討論的兩個函數被移到Movie class 內之后系統的class diagram ~~~ class Rental... double getCharge() { return _movie.getCharge(_daysRented); } ~~~ 搬移getCharge() 之后,我以相同手法處理常客積點計算。這樣我就把根據影片類型而變化的所有東西,都放到了影片類型所屬的class 中。 以下是重構前的代碼: ~~~ class Rental... int getFrequentRenterPoints() { if ((getMovie().getPriceCode() == Movie.NEW_RELEASE) && getDaysRented() > 1) return 2; else return 1; } ~~~ 重構如下: ~~~ Class rental... int getFrequentRenterPoints() { return _movie.getFrequentRenterPoints(_daysRented); } class movie... int getFrequentRenterPoints(int daysRented) { if ((getPriceCode() == Movie.NEW_RELEASE) && daysRented > 1) return 2; else return 1; } ~~~ 終于……我們來到繼承(Inheritance) 我們有數種影片類型,它們以不同的方式回答相同的問題。這聽起來很像subclasses 的工作。我們可以建立Movie 的三個subclasses ,每個都有自己的計費法(圖1.14)。 ![](https://box.kancloud.cn/2016-08-15_57b1b56a9156c.gif) 圖1.14 以繼承機制表現不同的影片類型 這么一來我就可以運用多態(polymorphism)來取代switch 語句了。很遺憾的是這里有個小問題,不能這么干。一部影片可以在生命周期內修改自己的分類,一個對象卻不能在生命周期內修改自己所屬的class。不過還是有一個解決方法:State pattern(模式)[Gang of Four]。運用它之后,我們的classes 看起來像圖1.15。 ![](https://box.kancloud.cn/2016-08-15_57b1b56ae48f6.gif) 圖1.15 運用State pattern(模式)表現不同的影片 加入這一層間接性,我們就可以在Price 對象內進行subclassing 動作(譯注:一如圖1.15),于是便可在任何必要時刻修改價格。 如果你很熟悉Gang of Four 所列的各種模式(patterns),你可能會問:『這是一個State 還是一個Strategy?』答案取決于Price class 究竟代表計費方式(此時我喜歡把它叫做Pricer 或PricingStrategy),或是代表影片的某個狀態(state,例如「Star Trek X 是一部新片」)。在這個階段,對于模式(和其名稱)的選擇反映出你對結構的想法。此刻我把它視為影片的某種狀態(state)。如果未來我覺得Strategy 能更好地說明我的意圖,我會再重構它,修改名字,以形成Strategy 。 為了引入State 模式,我使用三個重構準則。首先運用Replace Type Code with State/Strategy,將「與型別相依的行為」(type code behavior )搬移至State 模式內。然后運用Move Method 將switch 語句移到Price class 里頭。最后運用Replace Conditional with Polymorphism去掉switch 語句。 首先我要使用Replace Type Code with State/Strategy。第一步驟是針對「與 型別相依的行為」使用Self Encapsulate Field,確保任何時候都通過getting 和setting 兩個函數來運用這些行為。由于多數代碼來自其他classes,所以多數函數都己經使用getting 函數。但構造函數(constructor )仍然直接訪問價格代號(譯注:程序中的_priceCode): ~~~ class Movie... public Movie(String name, int priceCode) { _name = name; _priceCode = priceCode; } ~~~ 我可以用一個setting 函數來代替: ~~~ class Movie public Movie(String name, int priceCode) { _name = name; setPriceCode(priceCode); //譯注:這就是一個set method } ~~~ 然后編譯并測試,確保沒有破壞任何東西。現在我加入新class,并在Price 對象中提供「與型別相依的行為」。為了實現這一點,我在Price 內加入一個抽象函數(abstract method ),并在其所有subclasses 中加上對應的具體函數(concrete method): ~~~ abstract class Price { abstract int getPriceCode(); //取得價格代號 } class ChildrensPrice extends Price { int getPriceCode() { return Movie.CHILDRENS; } } class NewReleasePrice extends Price { int getPriceCode() { return Movie.NEW_RELEASE; } } class RegularPrice extends Price { int getPriceCode() { return Movie.REGULAR; } } ~~~ 現在我可以編譯這些新classes了。 現在,我需要修改Movie class 內的「價格代號」訪問函數(get/set函數,如下),讓它們使用新class。 下面是重構前的樣子: ~~~ public int getPriceCode() { return _priceCode; } public setPriceCode (int arg) { _priceCode = arg; } private int _priceCode; ~~~ 這意味我必須在Movie class 內保存一個Price 對象,而不再是保存一個_priceCode 變量。此外我還需要修改訪問函數(譯注:即get/set函數): ~~~ class Movie... public int getPriceCode() { //取得價格代號 return _price.getPriceCode(); } public void setPriceCode(int arg) { //設定價格代號 switch (arg) { case REGULAR: _price = new RegularPrice(); break; case CHILDRENS: _price = new ChildrensPrice(); break; case NEW_RELEASE: _price = new NewReleasePrice(); break; default: throw new IllegalArgumentException("Incorrect Price Code"); } } private Price _price; ~~~ 現在我可以重新編譯并測試,那些比較復雜的函數根本不知道世界巳經變了個樣兒。 現在我要對getCharge() 實施Move Method。 下面是重構前的代碼: ~~~ class Movie... double getCharge(int daysRented) { double result = 0; switch (getPriceCode()) { case Movie.REGULAR: result += 2; if (daysRented > 2) result += (daysRented - 2) * 1.5; break; case Movie.NEW_RELEASE: result += daysRented * 3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented > 3) result += (daysRented - 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 (daysRented > 2) result += (daysRented - 2) * 1.5; break; case Movie.NEW_RELEASE: result += daysRented * 3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented > 3) result += (daysRented - 3) * 1.5; break; } return result; } ~~~ 搬移之后,我就可以開始運用Replace Conditional with Polymorphism了。下面是重構前的代碼。 ~~~ class Price... double getCharge(int daysRented) { double result = 0; switch (getPriceCode()) { case Movie.REGULAR: result += 2; if (daysRented > 2) result += (daysRented - 2) * 1.5; break; case Movie.NEW_RELEASE: result += daysRented * 3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented > 3) result += (daysRented - 3) * 1.5; break; } return result; } ~~~ 我的作法是一次取出一個case 分支,在相應的class內建立一個覆寫函數(overriding method)。先從RegularPrice 開始: ~~~ class RegularPrice... double getCharge(int daysRented){ double result = 2; if (daysRented > 2) result += (daysRented - 2) * 1.5; return result; } ~~~ 這個函數覆寫(overrides)了父類中的case 語句,而我暫時還把后者留在原處不動。現在編譯并測試,然后取出下一個case 分支,再編譯并測試。(為了保證被執行的的確是subclass 代碼,我喜歡故意丟一個錯誤進去,然后讓它運行,讓測試失敗。噢,我是不是有點太偏執了?) ~~~ class ChildrensPrice 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); ~~~ 現在我可以運用同樣手法處理getFrequentRenterPoints()。重構前的樣子如下(譯注:其中有「與型別相依的行為」,也就是「判斷是否為新片」那個動作)。 ~~~ class Rental... 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.getFrequentRenterPoints(daysRented); } Class Price... int getFrequentRenterPoints(int daysRented) { if ((getPriceCode() == Movie.NEW_RELEASE) && daysRented > 1) return 2; else return 1; } ~~~ 但是這一次我不把superclass 函數聲明為abstract。我只是為「新片類型」產生一個覆寫函數(overriding method ),并在superclass 內留下一個已定義的函數,使它成為一種缺省行為。 ~~~ //譯注:在新片中產生一個覆寫函數(overriding method ) Class NewReleasePrice int getFrequentRenterPoints(int daysRented) { return (daysRented > 1) ? 2: 1; } //譯注:在superclass 內保留它,使它成為一種缺省行。 Class Price... int getFrequentRenterPoints(int daysRented){ return 1; } ~~~ 引入State 模式花了我不少力氣,值得嗎?這么做的收獲是:如果我要修改任何與價格有關的行為,或是添加新的定價標準,或是加入其他取決于價格的行為,程序的修改會容易得多。這個程序的其余部分并不知道我運用了State 模式。對于我目前擁有的這么幾個小量行為來說,任何功能或特性上的修改也許都稱不上什么困難,但如果在一個更復雜的系統中,有十多個與價格相關的函數,程序的修改難易度就會有很大的區別。以上所有修改都是小步驟進行,進度似乎太過緩慢,但是沒有任何一次我需要打開調試器(debugger),所以整個過程實際上很快就過去了。我書寫本章所用的時間,遠比修改那些代碼的時間多太多了。 現在我已經完成了第二個重要的重構行為。從此,修改「影片分類結構」,或是改變「費用計算規則」、改變常客積點計算規則,都容易多了。圖1.16和圖1.17描述State 模式對于價格信息所起的作用。 ![](https://box.kancloud.cn/2016-08-15_57b1b56b04590.gif) 圖1.16 運用State pattern (模式)當時的Interaction diagram ![](https://box.kancloud.cn/2016-08-15_57b1b56b1b831.gif) 圖1.17 加入State pattern (模式)之后的class diagram
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看