代碼重構(一):函數重構規則

重構是項目作到 必定程度後必然要作的事情。代碼重構,能夠改善既有的代碼設計,加強既有工程的可擴充、可維護性。隨着項目需求的不斷迭代,需求的不斷更新,咱們在項目中 所寫的代碼也在時時刻刻的在變化之中。在一次新的需求中,你添加了某些功能模塊,但這些功能模塊有可能在下一次需求中不在適用。或者你由於需求迭代與變 更,使你原有的方法或者類變得臃腫,以及各個模塊或者層次之間耦合度增長。此時,你要考慮重構了。html

 

重構,在《重構,改善既有代碼的設計》這本經典的書中給出了定義,大概就是:在不改變代碼對外的表現的狀況下,修改代碼的內部特徵。說白了,就是咱們的測試用例不變,而後咱們對既有的代碼的結構進行修改。重構在軟件開發中是常常遇到的,也是很是重要的。在需求迭代,Debug,Code Review時,你均可以對你既有的代碼進行重構。編程

 

在接下來的幾篇博文中,我想與你們一塊去窺探一下代碼重構的美麗,學習一下代碼重構的一些規則。固然在每一個規則中都有小的Demo, 在本篇博客以及相關內容的博客是使用Swift語言實現的。固然,和設計模式相同,重構重要的是手法和思想,和使用什麼樣的語言關係不大。經典的重構書籍中是使用Java語言來實現的,若是你對PHP, Python等其餘語言比較熟悉,徹底可使用這些語言來測試一些重構手法。設計模式

 

本篇博客的主題就是經過一些列的重構手法,對既有的須要重構的函數或者方法進行重 構。而且會將每一個示例在GitHub上進行分享,感興趣的小夥伴能夠對其進行下載。有的小夥伴說了,我沒有Mac,怎麼對你寫的Swift代碼進行編譯 呢?這個問題好解決,你能夠看我以前發表的這篇博客窺探Swift之使用Web瀏覽器編譯Swift代碼以及Swift中的泛型。你能夠將相關代碼進行拷貝,在瀏覽器中觀察結果。由於在線編譯的網站是國外的,訪問起來也許會有一些卡頓,不過是能夠用的。好前面扯了這麼多了,進入今天的主題。瀏覽器

目錄:函數

1、將大函數按模塊拆分紅幾個小的函數post

2、將微不足道的小函數經過inline進行整合單元測試

3、將一些臨時變量使用函數替換(以函數查詢替代臨時變量)學習

4、將複雜表達式拆分紅多個表達式(以多個解釋性臨時變量拆分複雜表達式)測試

5、將在不一樣語義下具備不一樣含義的臨時行變量進行拆分網站

6、移除對函數參數的賦值(引入另外一個臨時性變量)

7、以對象取代函數

 

1、Extract Method(提取函數)-------將大函數按模塊拆分紅幾個小的函數

Extract Method被翻 譯成中文就是提取函數的意思,這一點在代碼重構中用的很是很是的多。在重構時提倡將代碼模塊進行細分,由於模塊越小,可重用度就越大。不要寫大函數,若是 你的函數過大,那麼這意味着你的函數須要重構了。由於函數過大,可維護性,可理解性就會變差。而且當你實現相似功能的時候就容易產生重複代碼。寫代碼時, 最忌諱的就是代碼重複。這也就是常常所說的DRY(Don`t Repeat Yourself)原則。因此當函數過長時,你須要將其細分,將原函數拆分紅幾個函數。

 

下 方將會經過一個示例來直觀的感覺一下Extract Method,固然這些示例不是我原創的,是《重構:改善既有代碼的設計》中Java示例演變的Swift版,在寫Swift代碼時,對原有的示例進行了 一些修改,算是僞原創吧。不過目的只有一個:但願與你們交流分享。實在是沒有必要再找其餘的例子說明這些重構規則,由於《重構:改善既有的代碼的設計》這 本書真的是太經典了。

 

1.須要重構的代碼以下所示。下方代碼中的MyCustomer類中有兩個常量屬性,而且該類提供了一個構造器。該類還提供了一個輸出方法,就是第一該類中的屬性進行打印說明,其實該類中沒有什麼功能。

  

 

在寫好須要重構的類後,咱們要爲該類寫一個測試用例。這便於在咱們重構時對重構的 正確性進行驗證,由於每次重構後都要去執行該測試用例,以保證咱們重構是正確的。下方截圖就是爲上方示例寫的測試用例以及該測試用例的打印結果。固然重構 後咱們也須要調用該測試用例,並觀察打印結果是否與以前的一致。固然若是你不想本身觀察,你能夠爲上面的類添加相應的單元測試,這也是在常規項目中常常使 用的。至於若是添加測試用例,咱們會在後面的博客中進行詳細介紹。下方就是上述類的測試用例和輸出結果:

      

 

2.接下來咱們對上面類中的printOwning函數進行分析。上述類能夠正常 工做,這是確定的。可是printOwning()函數寫的不夠好。由於它幹了太多的事情,也就是說該函數包括了其餘子模塊,須要對其進行拆分。由上面截 圖中的紅框能夠看出,每一個紅框就表明着一個獨立的功能模塊,就說明這一塊代碼能夠被拆分紅獨立的函數。在拆分子函數時,咱們要爲該函數起一個與改代碼塊功 能相符合的名字。也就是說當你看到該函數名字時,你就應該知道該函數是幹嗎的。

 

下方代碼段就是咱們重構後的類。說白的,就是對函數中能夠進行獨立的模塊進行提 取,併爲提取的新的函數命一個合適的名稱。通過重構後printOwing()函數就只有兩行代碼,這樣看其中調用的函數名也是很容易理解其做用的。下方 拆分出來的三個函數也是一個獨立的模塊,由於函數短小,因此易於理解,同時也易於重用。通過Extract Method,固然好處是多多的。通過重構後的代碼,我在調用上述的測試用例,輸出結果和原代碼是一直的,若是不一致的話,那麼說明你的重構有問題呢,需 要你進行Debug。

     

 

 

二. Inline Method ---- 內聯函數:將微不足道的小函數進行整合

看過《周易》的小夥伴應該都知道,《周易》所表達的思想有一點就是「物極必反」。 《周易》中的六十四卦中的每一卦的「上九」(第六位的陽爻)或者「上六」(第六位的陰爻)都是物極必反的表現。其實《周易》其實就是計算機科學中二進制的 表象,由於太極生兩儀(2進制中的2),兩儀生四象(2的平方爲4),四象生八卦(4 x 2 = 8),八卦有演變出六十四卦。六十四卦的就是2進制中的0-1排列。九五至尊,九六就物極必反了。wo kao, 扯遠了,言歸正傳,固然這提到《周易》不是說利用周易如何去算卦,如何去預測,本寶寶不信這東西。不過《周易》中的哲學仍是頗有必要學習一下的。有所取, 有所不取。

 

回到本博客的主題,Inline Method實際上是和Extract Method相對的。當你在重構或者平時編程時,對模塊進行了過分的封裝,也就是使用Extract Method有點過頭了,把過於簡單的東西進行了封裝,好比一個簡單的布爾表達式,並且該表達式只被用過一次。此時就是過分的使用Extract Method的表現了。物極必反,因此咱們須要使用Inline Method進行中和,將過分封裝的函數在放回去,或者將那些沒有必要封裝的函數放回去。也就是Extract Method相反的作法。

至於Inline Method規則的示例呢,在此就不作過多的贅述了,由於只須要你將Extract Method的示例進行翻轉便可。

 

三.Replace Temp with Query----以查詢取代臨時變量: 將一些臨時變量使用函數替代

1.Replace Temp with Query說白了就是將那些有着複雜表達式賦值而且屢次使用的臨時變量使用查詢函數取代,也就是說該臨時變量的值是經過函數的返回值來獲取的。這樣一來在 實現相似功能的函數時,這些複雜的臨時變量就能夠進行復用,從而減小代碼的重複率。下方就是Replace Temp with Query規則的一個特定Demo,接下來咱們要對getPrice()函數使用Replace Temp with Query規則進行重構。

  

 

 

對上面的小的demo建立對應的測試用例是少不了的,由於咱們要根據測試用例還測試我重構後的代碼是否一致,下方截圖就是該代碼的測試用例以及輸出結果,具體以下所示。

   

 

2.接下來就是對Procut類中的getPrice()函數進行分析並重構了。在getPrice()函數中的第一個紅框中有一個 basePrice臨時常量,該常量有一個較爲複雜的賦值表達式,咱們能夠對其使用Replace Temp with Query進行重構,可就是建立一個函數來返回該表達式的值。第二個紅框中的discountFactor臨時變量被屢次使用,咱們能夠對其經過 Replace Temp with Query規則進行重構,具體重構後的代碼以下所示。

由重構後的代碼容易看出,上面咱們提到的臨時常量或者變量都不存在了,取而代之的是兩個查詢方法,對應的查詢方法返回的就是以前消除的臨時變量或常量的值。

   

 

4、Inline Temp ---內聯臨時變量:與上面的Replace Temp with Query相反

當臨時變量只被一個簡單的表達式賦值,而該臨時變量妨礙了其餘重構手法。此時咱們就不該 該使用Replace Temp with Query。之因此有時咱們會使用到Inline Temp規則,是由於Replace Temp with Query規則使用過分形成的狀況,仍是物極必反,使用Replace Temp with Query過分時,就須要使用Inline Temp進行修正,固然Inline Temp的示例與Replace Temp with Query正好相反,在此就不作過多的贅述了。

 

5、Introduce Explaining Variable---引入解釋性變量:將複雜的表達式拆分紅多個變量

當一個函數中有一個比較複雜的表達式時,咱們能夠將 表達式根據功能拆分紅不一樣的變量。拆分後的表達式要比以前未拆分的表達式的可讀性更高。將表達式拆分紅相應的臨時變量,也就是Introduce Explaining Variable,若是臨時變量被屢次使用的話,咱們還能夠嘗試着使用Replace Temp with Query規則去除臨時變量,也就是將臨時變量換成查詢函數。

1.在下方Product類中的getPrice()方法中返回了一個比較長的表達式,第一眼看這個函數感受會很是的不舒服。由於它返回的表達式太長了,並且可讀性不太好。在這種狀況下就頗有必要將該表達式進行拆分。

   

 

2.接下來就可使用Introduce Explaining Variable規則,引入解釋性變量。顧名思義,咱們引入的變量是爲了解釋該表達式中的一部分的功能的,目的在於讓該表達式具備更好的可讀性。使用 Introduce Explaining Variable規則,就至關於爲該表達式添加上相應的註釋。下方截圖就是使用 Introduce Explaining Variable規則進行重構後的結果。

   

 

3.引入臨時變量是爲了更好的可讀性,若是臨時變量所表明的表達式屢次使用,咱們就能夠對上述函數在此使用Replace Temp with Query規則進行重構。也就是去除常用並且表達式比較複雜的臨時變量,下方代碼段是對上述函數進行Replace Temp with Query重構,去掉臨時變量,再次重構後的結果以下所示。

    

 

6、Split Temporary Variable-----分解臨時變量:一心不可二用

什麼叫分解臨時變量的,具體說來就是在一個函數中一個臨時變量不能作兩種事 情,也就是一個臨時變量不能賦上不一樣意義的值。若是你這麼作了,那麼對不起,請對該重複使用的臨時變量進行分解,也就是說你須要建立一個新的臨時變量來接 收第二次分配給第一個臨時變量的值,併爲第二個臨時變量命一個確切的名字。

下方第一個函數是重構前的,能夠看出temp被重複的賦值了兩次的值,若是 這兩個值關係不大,並且temp不足以對兩個值的意思進行說明。那麼就說明該段代碼就應該被重構了。固然,重構的作法也是很是簡單的,只須要術業有專攻即 可,各司其職,而且爲每一個臨時變量命一個合適的名字便可。具體作法以下所示。

 

 

 7、Remove Assignments to Parameters----移除對參數的賦值

「移除對參數的賦值」是什麼意思呢?顧名思義,就是在函數中不要對函數參數 進行賦值。也就是說你在函數的做用域中不要對函數的參數進行賦值(固然,輸入輸出參數除外),當直接對函數的參數進行修改時,對不起,此時你應該對此重 構。由於這樣會是參數的原始值丟失,咱們須要引入臨時變量,而後對這個臨時變量進行操做。

1.下方這個discount()函數就作的很差,由於在 discount()函數中直接對非inout參數inputVal進行了修改而且返回了,咱們不建議這樣作。遇到這種狀況,咱們須要使用Remove Assignments to Parameters規則對下方的函數進行重構。

   

  

2.固然重構的手法也特別簡單,就是須要將上面的inputVal使用函數的臨時變量進行替代便可,下方就是重構後的函數。

   

 

八.Replace Method with Method Object----以函數對象取代函數

當一個特別長的函數,並且函數中含有比較複雜的臨時變量,使用上述那些方法 很差進行重構時,咱們就要考慮將該函數封裝成一個類了。這個對應的類的對象就是函數對象。咱們能夠將該場函數中的參數以及臨時變量轉變成類的屬性,函數要 作的事情做爲類的方法。將函數轉變成函數類後,咱們就可使用上述的某些方法對新的類中的函數進行重構了。具體作法請看下方示例。

1.下方示例中的discount函數有過多的參數(固然,現實項目工程中參數比這個還要多),並函數中含有多個臨時變量,假設函數功能比較複雜,並且比較長。下方示例對該函數使用上述那些規則進行重構會比較複雜,此時咱們就能夠將該函數抽象成一個類。

   

 

2.重構的第一步就是將上述discount()函數抽象成Discount類。在Discount類中有六個屬性,這六個屬性分別對應着 discount()函數的不一樣參數。除了添加參數屬性外,咱們在函數類提取時還添加了一個Account的委託代理對象。該委託代理對象是爲了在 Discount類中訪問Account類中依賴的數據,下方是第一次重構後的代碼。

   

 

3.接着,咱們就能夠在新的Discount類中的compute()方法中使用 咱們上述介紹的規則進行重構。對compute()方法進行分析,咱們發現importandValue等屬性是能夠經過Replace Temp with Qurey 規則進行消除的。所爲咱們能夠再次對上述方法進行重構,重構後的具體代碼以下:

     

 

今天的博客主要講了如何對既有代碼中的函數進行重構,在本篇博客中提到了8大規 則。這8大規則在函數代碼重構時時很是實用的,而且也是很是重要的。仍是那句話,雖然代碼是使用Swift語言實現的,可是代碼重構的手法和思想和語言無 關。接下來還會繼續更新關於代碼重構的博客,敬請期待吧。

相關文章
相關標籤/搜索