<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>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ### Extract Method(提煉函數) 你有一段代碼可以被組織在一起并獨立出來。 將這段代碼放進一個獨立函數中,并讓函數名稱解釋該函數的用途。 ~~~ void printOwing(double amount) { printBanner(); //print details System.out.println ("name:" + _name); System.out.println ("amount" + amount); } ~~~ => ~~~ void printOwing(double amount) { printBanner(); printDetails(amount); } void printDetails (double amount) { System.out.println ("name:" + _name); System.out.println ("amount" + amount); } ~~~ **動機(Motivation)** Extract Method是我最常用的重構手法之一。當我看見一個過長的函數或者一段需要注釋才能讓人理解用途的代碼,我就會將這段代碼放進一個獨立函數中。 有數個原因造成我喜歡簡短而有良好命名的函數。首先,如果每個函數的粒度都很小(finely grained),那么函數之間彼此復用的機會就更大;其次,這會使高層函數碼讀起來就像一系列注釋;再者,如果函數都是細粒度,那么函數的覆寫(overridden)也會更容易些。 的確,如果你習慣看大型函數,恐怕需要一段時間才能適應這種新風格。而且只有當你能給小型函數很好地命名時,它們才能真正起作用,所以你需要在函數名稱下點功夫。人們有時會問我,一個函數多長才算合適?在我看來,長度不是問題,關鍵在于函數名稱和函數本體之間的語義距離(semantic distance )。如果提煉動作 (extracting )可以強化代碼的清晰度,那就去做,就算函數名稱比提煉出來的代碼 還長也無所謂。 **作法(Mechanics)** - 創造一個新函數,根據這個函數的意圖來給它命名(以它「做什么」來命名, 而不是以它「怎樣做」命名)。 - 即使你想要提煉(extract )的代碼非常簡單,例如只是一條消息或一個函數調用,只要新函數的名稱能夠以更好方式昭示代碼意圖,你也應該提煉它。但如果你想不出一個更有意義的名稱,就別動。 - 將提煉出的代^碼從源函數(source)拷貝到新建的目標函數(target)中。 - 仔細檢查提煉出的代碼,看看其中是否引用了「作用域(scope)限于源函數」的變量(包括局部變量和源函數參數)。 - 檢查是否有「僅用于被提煉碼」的臨時變量(temporary variables )。如果有,在目標函數中將它們聲明為臨時變量。 - 檢查被提煉碼,看看是否有任何局部變量(local-scope variables )的值被它改變。如果一個臨時變量值被修改了,看看是否可以將被提煉碼處理為一個查詢(query),并將結果賦值給相關變量。如果很難這樣做,或如果被修改的 變量不止一個,你就不能僅僅將這段代碼原封不動地離煉出來。你可能需要先使用 Split Temporary Variable,然后再嘗試提煉。也可以使用Replace Temp with Query 將臨時變量消滅掉(請看「范例」中的討論)。 - 將被提煉碼中需要讀取的局部變量,當作參數傳給目標函數。 - 處理完所有局部變量之后,進行編譯。 - 在源函數中,將被提煉碼替換為「對目標函數的調用」。 - 如果你將任何臨時變量移到目標函數中,請檢查它們原本的聲明式是否在被提煉碼的外圍。如果是,現在你可以刪除這些聲明式了。 - 編譯,測試。 **范例(examples):無局部變量(No Local Variables)** 在最簡單的情況下,Extract Method 易如反掌。請看下列函數: ~~~ void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; // print banner System.out.println ("**************************"); System.out.println ("***** Customer Owes ******"); System.out.println ("**************************"); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } //print details System.out.println ("name:" + _name); System.out.println ("amount" + outstanding); } ~~~ 我們可以輕松提煉出「打印banner」的代碼。我只需要剪切、粘貼、再插入一個函數調用動作就行了: ~~~ void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; printBanner(); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } //print details System.out.println ("name:" + _name); System.out.println ("amount" + outstanding); } void printBanner() { // print banner System.out.println ("**************************"); System.out.println ("***** Customer Owes ******"); System.out.println ("**************************"); } ~~~ **范例(Examples):有局部變量(Using Local Variables)** 果真這么簡單,這個重構手法的困難點在哪里?是的,就在局部變量,包括傳進源函數的參數和源函數所聲明的臨時變量。局部變量的作用域僅限于源函數,所以當我使用Extract Method 時,必須花費額外功夫去處理這些變量。某些時候它們甚至可能妨礙我,使我根本無法進行這項重構。 局部變量最簡單的情況是:被提煉碼只是讀取這些變量的值,并不修改它們。這種情況下我可以簡單地將它們當作參數傳給目標函數。所以如果我面對下列函數: ~~~ void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; printBanner(); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } //print details System.out.println ("name:" + _name); System.out.println ("amount" + outstanding); } ~~~ 我就可以將「打印詳細信息」這一部分提煉為「帶一個參數的函數」: ~~~ 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 printDetails (double outstanding) { System.out.println ("name:" + _name); System.out.println ("amount" + outstanding); } ~~~ 必要的話,你可以用這種手法處理多個局部變量。 如果局部變量是個對象,而被提煉碼調用了會對該對象造成修改的函數,也可以如法炮制。你同樣只需將這個對象作為參數傳遞給目標函數即可。只有在被提煉碼真的對一個局部變量賦值的情況下,你才必須采取其他措施。 **范例(Examples):對局部變量再賦值(Reassigning a Local Variable)** 如果被提煉碼對局部變量賦值,問題就變得復雜了。這里我們只討論臨時變量的問題。如果你發現源函數的參數被賦值,應該馬上使用Remove Assignments to Parameters。 被賦值的臨時變量也分兩種情況。較簡單的情況是:這個變量只在被提煉碼區段中使用。果真如此,你可以將這個臨時變量的聲明式移到被提煉碼中,然后一起提煉出去。另一種情況是:被提煉碼之外的代碼也使用了這個變量。這又分為兩種情況: 如果這個變量在被提煉碼之后未再被使用,你只需直接在目標函數中修改它就可以了;如果被提煉碼之后的代碼還使用了這個變量,你就需要讓目標函數返回該變量改變后的值。我以下列代碼說明這幾種不同情況: ~~~ 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(); 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 減少臨時變量。如果即使這么做了提煉依舊困難重重,我就會動用 Replace Method with Method Object,這個重構手法不在乎代碼中有多少臨時變量,也不在乎你如何使用它們。
                  <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>

                              哎呀哎呀视频在线观看