ivaneeo's blog

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

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            669 Posts :: 0 Stories :: 64 Comments :: 0 Trackbacks

          #

          作法(Mechanics)

          首先是簡單情況:

            • 找出只被賦值一次的臨時變量==>如果某個臨時變量被賦值超過一次,考慮使用Split Temporay Variable(128)將它分割成多個變量.
            • 將該臨時變量聲明為final.
            • 編譯.==>這可確保該臨時變量的確只被賦值一次.
            • 將[對臨時變量賦值]之語句的等號右側部分提煉到一個獨立函數中.
                • ==>首先將函數聲明為private.日后你可能會發現有更多class需要使用它,彼時你可輕易放松對它的保護.
                • ==>確保提煉出來的函數無任何連帶影響(副作用),也就是說該函數并不修改任何對象內容.如果它有連帶影響,就對它進行Separate Query from Modifier(279).
            • 編譯,測試.
            • 在該臨時變量身上實施Inline Temp(119).

          我們常常使用臨時變量保存循環中的累加信息.在這種情況下,整個循環都可以被提煉為一個獨立函數,這也使原本的函數可以少掉幾行擾人的循環代碼.有時候,你可能會用單一循環累加好幾個值.這種情況下你應該針對每個累加值重復一遍循環,這樣就可以將所有臨時變量都替換為查詢式(query).當然,循環應該很簡單,復制這些代碼時才不會帶來危險.

          運用此手法,呢可能會擔心性能問題.和其他性能問題一樣,我們現在不管它,因為它十有八九根本不會造成任何影響.如果性能真的出了問題,你也可以在優化時期解決它.如果代碼組織良好,那么你往往能夠發現更有效的優化法案;如果你沒有進行重構,好的優化法案就可能與你失之交臂.如果性能實在太糟糕,要把臨時變量放回去也是很容易的.

          posted @ 2005-08-25 15:04 ivaneeo 閱讀(230) | 評論 (0)編輯 收藏

          動機(Motivation)
          臨時變量的問題在于:它們是暫時的,而且只能在所屬函數內使用.由于臨時變量只有在所屬函數內才可見,所以它們會驅使你寫出更長的函數,因為只有這樣你才能訪問到想要訪問的臨時變量.如果把臨時變量替換為一個查詢式(query method),那么同一個class中的所有函數都將可以獲得這份信息.這將帶給你極大幫助,使你能夠為這個class編寫更清晰的代碼.

          Replace Temp with Query(120)往往是你運用Extract Method(110)之前必不可少的一個步驟.局部變量會使代碼難以被提煉,所以你應該盡可能把它們替換為查詢式.

          這個重構手法較為直率的情況就是:臨時變量只被賦值一次,或者賦值給臨時變量的表達式不受其他條件影響.其他情況比較棘手,但也有可能發生.你可能需要先運用Split Temporary Variable(128)或Separate Query from Modifier(279)使情況變得簡單一些,然后再替換臨時變量.如果你想替換的臨時變量是用來收集結果的(例如循環中的累加值),你就需要將某些程序邏輯(例如循環)拷貝到查詢式(query method)去.
          posted @ 2005-08-25 14:43 ivaneeo 閱讀(269) | 評論 (0)編輯 收藏

          你的程序以一個臨時變量(temp)保存某一表達式的運算結果.

          將這個表達式提煉到一個獨立函數中.將這個臨時變量的所有[被引用點]替換為[對新函數的調用].新函數可被其他函數使用.

          double basePrice = _quantity * _itemPrice;
          if(basePrice > 1000)
             return basePrice * 0.95;
          else
             return basePrice * 0.98;
                                              |   |
                                              |   |
                                             \    /
          if(basePrice() > 1000)
             return basePrice() * 0.95;
          else
             return base() * 0.98;
          ...
          double basePrice() {
             return _quantity * _itemPrice;
          }
          posted @ 2005-08-25 14:22 ivaneeo 閱讀(172) | 評論 (0)編輯 收藏

            • 作法(Mechanics)
            • 如果這個臨時變量并未被聲明為final,那就將它聲明為final,然后編譯.==>這可以檢查該臨時變量是否真的只被賦值一次.
            • 找到該臨時變量的所有引用點,將它們替換為[為臨時變量賦值]之語句中的等號右側表達式.
            • 每次修改后,編譯并測試.
            • 修改完所有引用電之后,刪除該臨時變量的聲明式和賦值語句.
            • 編譯,測試.
          posted @ 2005-08-25 14:15 ivaneeo 閱讀(148) | 評論 (0)編輯 收藏

          動機(Motivation)
          Inline Temp(119)多半是作為Replace Temp with Query(120)的一部分來使用,所以真正的動機出現在后者那兒.唯一單獨使用Inline Temp(119)的情況是:你發現某個臨時變量被賦予某個函數調用的返回值.一般來說,這樣的臨時變量不會有任何危害,你可以放心地把它留在那兒.但如果這個臨時變量妨礙了其他的重構手法--例如Extract Method(110),你就應該就它inline化.

          posted @ 2005-08-25 13:53 ivaneeo 閱讀(186) | 評論 (0)編輯 收藏

          你有一個臨時變量,只被一個簡單表達式賦值一次,而它妨礙了其他重構方法.

          將所有對該變量的引用動作,替換為對它賦值得那個表達式自身.

          double basePrice = anOrder.basePrice();
          return (basePrice > 1000);
                                        |   |
                                        |   |
                                       \    /
          return (anOrder.basePrice() > 1000);
          posted @ 2005-08-25 13:52 ivaneeo 閱讀(226) | 評論 (0)編輯 收藏

            • 作法(Mechanics)
            • 檢查函數,確定它不具多態性(is not polymorphic).==>如果subclass繼承了這個函數,就不要將此函數inline化,因為subclass無法覆寫(override)一個根本不存在的函數.
            • 找出這個函數的所有被調用點.
            • 將這個函數的所有被調用點都替換為函數本體(代碼).
            • 編譯,測試.
            • 刪除該函數的定義.

          被我這樣一寫,Inline Method(117)似乎很簡單.但情況往往并非如此.對于遞歸調用,多返回點,inline至另一個對象中而該對象并無提供訪問函數(accessors)......,每一種情況我都可以寫上好幾頁.我之所以不寫這些特殊情況,原因很簡單:如果你遇到了這樣的復雜情況,那么就不應該使用這個重構手法.

          posted @ 2005-08-24 17:28 ivaneeo 閱讀(196) | 評論 (0)編輯 收藏

          動機(Motivation)
          有時候你會遇到某些函數,其內部代碼和函數名稱同樣清晰易讀.

          另一種需要使用Inline Method(117)的情況是:你手上有一群組織不甚合理的函數.你可以將它們都inline到一個大型函數中,再從中提煉出組織合理的小型函數.Kent Beck發現,實施Replace Method with Method Object(135)之前先這么做,往往可以獲得不錯的效果.你可以把你所要的函數(有著你要的行為)的所有調用對象的函數內容都inline到method object(函數對象)中.比起既要移動一個函數,又要移動它所調用的其他所有函數,[將大型函數作為單一整體來移動]會比較簡單.

          如果別人使用了太多間接層,使得系統中的所有函數都似乎只是對另一個函數的簡單委托(delegation),造成我在這些委托動作之間暈頭轉向,那么我通常都會使用Inline Method(117).當然,間接層有其價值,但不是所有間接層都有價值.試著使用inlining,我可以找出那些有用的間接層,同時將那些無用的間接層去除.
          posted @ 2005-08-24 17:12 ivaneeo 閱讀(192) | 評論 (0)編輯 收藏

          一個函數,其本體(method body)應該與其名稱(method name)同樣清楚易懂.

          在函數調用點插入函數本體,然后移除該函數.

          int getRating() {
             return (moreThanFiveLateDeliveries()) ? 2 : 1;
          }
          boolean moreThanFiveLateDeliveries() {
             return _numberOfLateDeliveries > 5;
          }
                                               |   |
                                               |   |
                                              \    /

          int getRating() {
             return (_numberOfLateDeliveries > 5) ? 2 : 1;
          }
          posted @ 2005-08-24 16:55 ivaneeo 閱讀(183) | 評論 (0)編輯 收藏

          范例(Examples):局部變量再賦值(Reassigning)
          如果被提煉碼對局部變量賦值,問題就變得復雜了.這里我們只討論臨時變量的問題.如果你發現源函數的參數被賦值,應該馬上使用Remove Assignments to Parameters(131).

          被賦值的臨時變量也分兩種情況.較簡單的情況是:這個變量只在被提煉碼區段中使用.果真如此,你可以將這個臨時變量的聲明式移到被提煉碼中,然后一起提煉出去.另一種情況是:被提煉碼之外的代碼也使用了這個變量.這又分為兩種情況:如果這個變量在被提煉碼之后未再被使用,你只需直接在目標函數中修改它就可以了;如果被提煉碼之后的代碼還使用了這個變量,你就需要讓目標函數返回該變量改變后的指.我以下列代碼說明這幾種不同情況:
          void printOwing() {
              Enumeration e = _orders.elements();
              double outstanding = 0.0;

             printBanner();

              // calculate outstanding
              while(e.hasMoreElements()) {
                 Order each = (Order) e.nextElement();
                 outstanding += each.getAmount();
              }

             printDetails(outstanding);
          }

          現在我把[計算]代碼提煉出來:

          void printOwing() {
             printBanner();
             double outstanding = getOutstanding();
             printDetails(outstanding);
          }

          double getOutstanding() {
                 Enumeration e = _orders.elements();
                 double outstanding = 0.0;

                 while(e.hasMoreElements()) {
                    Order each = (Order) e.nextElement();
                    outstanding += each.getAmount();
                 }
                return outstanding;
          }

          Enumeration變量e只在被提煉碼中用到,所以我可以將它整個搬到新函數中.double變量outstanding在被提煉碼內外都被用到,所以我必須讓提煉出來的新函數返回它.編譯測試完成后,我就把傳值改名,遵循我的一貫命名原則:

          double getOutstanding() {
                 Enumeration e = _orders.elements();
                 double result = 0.0;

                 while(e.hasMoreElements()) {
                    Order each = (Order) e.nextElement();
                    result += each.getAmount();
                 }
                return result;
          }

          本例中的outstanding變量只是很單純地被初始化為一個明確初值,所以我可以只在新函數中對它初始化.如果代碼還對這個變量做了其他處理,我就必須將它的值作為參數傳給目標函數.對于這種變化,最初代碼可能是這樣:

          void printOwing(double previousAmount) {
              Enumeration e = _orders.elements();
              double outstanding = previousAmount * 1.2;

             printBanner();

              // calculate outstanding
              while(e.hasMoreElements()) {
                 Order each = (Order) e.nextElement();
                 outstanding += each.getAmount();
              }

             printDetails(outstanding);
          }

          提煉后的代碼可能是這樣:

          void printOwing(double previousAmount) {
             double outstanding = previousAmount * 1.2;

             printBanner();
             double outstanding = getOutstanding(outstanding);
             printDetails(outstanding);
          }
          double getOutstanding(double initialValue) {
                 double result = initialValue;
                 Enumeration e = _orders.elements();
                 

                 while(e.hasMoreElements()) {
                    Order each = (Order) e.nextElement();
                    result += each.getAmount();
                 }
                return result;
          }
          編譯并測試后,我再將變量outstanding的初始化過程整理一下:
          void printOwing(double previousAmount) {
             printBanner();
             double outstanding = getOutstanding(previousAmount * 1.2);
             printDetails(outstanding);
          }

          這時候,你可能會問:[如果需要返回的變量不止一個,又該怎么辦?]

          你有數種選擇.最好的選擇通常是:挑選另一塊代碼來提煉.我比較喜歡讓每個函數都只返回一個值,所以我會安排多個函數,用以返回多個值.如果你使用的語言支持[輸出式參數](output parameters),你可以使用它們帶回多個回傳值.但我還是盡可能選擇單一返回值.

          臨時變量往往為數眾多,甚至會使提煉工作舉步維艱.這種情況下,我會嘗試先運用Replace Temp with Query(120)減少臨時變量.如果即使這么做了提煉依舊困難重重,我就會動用Replace Method with Method Object(135),這個重構手法不在乎代碼中有多少臨時變量,也不在乎你如何使用它們.

          posted @ 2005-08-24 16:14 ivaneeo 閱讀(220) | 評論 (0)編輯 收藏

          僅列出標題
          共67頁: First 上一頁 48 49 50 51 52 53 54 55 56 下一頁 Last 
          主站蜘蛛池模板: 平武县| 成都市| 高青县| 肇庆市| 烟台市| 兰考县| 平山县| 海盐县| 仁化县| 永登县| 洛扎县| 玛多县| 平山县| 育儿| 三河市| 屯门区| 小金县| 辰溪县| 玉山县| 江都市| 平罗县| 济南市| 灵武市| 达州市| 遂昌县| 乌海市| 林芝县| 奈曼旗| 广丰县| 开远市| 泗洪县| 长顺县| 大厂| 铁岭县| 清镇市| 千阳县| 乌苏市| 孟村| 临漳县| 华亭县| 贵南县|