在上篇博客《代碼重構(一):函數重構規則(Swift版)》中,詳細的介紹了函數的重構規則,其中主要包括:Extract Method, Inline Method, Inline Temp, Replace Temp with Query, Introduce Explaining Variable, Split Temporary Variable, Remove Assignments to Parameters, Replace Method with Method Object等。關於上述這些函數重構的規則更為詳細的信息請參考上一篇博客,在此就不做過多的贅述了。
今天這篇博客主要介紹一下類的重構。在我們寫代碼時,有些類是不規范的,需要重構。在對類進行重構時,也是有一些章法可尋的,本篇博客就結合着相關示例,對類的重構進行相關的介紹。當然在本篇博客中使用的實例,還是延續上一篇文章的風格,仍然采用Swift語言進行編寫。當然,還是那句話,重構的思想和手法與設計模式類似,都與具體語言實現無關。觸類旁通,關鍵還是思想和手法。為了精簡博文的篇幅,相關的測試用例就不往上粘貼了。當然,在你實現時,測試用例是必不可少的,因為測試用例可以在你重構時及時發現因為重構而產生的錯誤。言歸正傳,進入今天博客的主題。
一、Move Method----方法遷移
關於Move Method,首先談論一下為什么要進行方法的遷移。原因很簡單,就是當類中的方法不適合放在當前類中時,就應該為該方法尋找合適下家。那么怎樣才可以稱作是當前方法不適合在當前類中呢?一個類中的函數與另一個類有很多的交互,函數非常依賴於某個類。如果一個類有太多行為,或者與另一個類有太多合作而形成高度耦合。此時就應該將該方法搬移到其高度依賴的類中。
在給方法搬家時需要做的就是在方法的新家中創建一個方法,實現要搬移的功能,如果新創建的函數需要舊類中的數據,那么就創建一個委托對象來解決這個問題。說白了就是在另一個類中創建一個相同的功能的新函數,將舊函數變成一個單純的委托函數,或者將舊函數完全移除。搬移后,我們可以再使用函數的重構規則對新組的函數進行重構。下方就通過一個實例來直觀的感受一下Move Method。
1.代碼實例
在下方截圖中有兩個類,一個Book類,另一個是BookCustomer類。在Book類中有兩個屬性,一個是bookCode:表示書的種類(NEW_BOOK,OLD_BOOK, CHIDREN_BOOK), 另一個屬性就是書名bookName。在BookCustomer中有3個字段,name表示用戶的名稱,isVip表示用戶是否是會員,books表示該用戶所購買的書的集合。BookCustomer類中的charge()方法用來根據books數組來計算圖書的總價格,並返回總價格。如果是VIP, 就在總價格的基礎上打7折,普通用戶打8折。下方截圖就是其具體實現。
2.使用Move Method進行重構
首先我們對上述兩個類進行分析,觀察需要重構的地方。首先第一眼看代碼時,較長的charge()函數會讓我們看起來些微的不舒服,因為它太長了。再仔細分析,其中的Switch語句中的業務邏輯用的全是Book類的東西,和當前BookCustomer類沒用什么關聯。但是這個Switch語句是當前charge()函數的核心,也就是BookCustomer嚴重依賴Book類的地方。以此分析下去,我們就清楚的指定,該Switch語句塊放錯了地方,它應該放在Book類中。所以我們應該將這塊代碼進行搬移。
重構方法就是在Book類中創建一個charge()函數,將Switch語句塊放入新的charge()函數中。然后在原來的charge()函數使用Switch語句時調用新的charge()方法。下方代碼段是使用Move Method重構后的結果。
3.使用函數重構
在使用Move Method重構后,我們看出在BookCustomer類中的charge()函數是可以使用Extract Method和Replace Temp With Qurey進行重構的。關於這兩個函數重構的規則的具體細節請參見《代碼重構(一):函數重構規則(Swift版)》中的介紹。下方截圖是對BookCustomer類中的charge()函數進行重構后的結果,如下所示:
二、Move Field----搬移字段
上一部分是搬移方法,Move Field(搬移字段)與Move Method適用場景類似。當在一個類中的某一個字段,被另一個類的對象頻繁使用時,我們就應該考慮將這個字段的位置進行更改了。Move Field與Move Method的思想和做法差不多,再次對其的示例就省略了。舉一反三,你可以類比着Move Method來使用Move Field規則。具體實現方式在此就不做過多的贅述了。
三、Extract Class----提煉類
Extract Class和Extract Method類似,Extract Method提取的是方法,而Extract Class提取的是類。一個類如果過於復雜,做了好多的事情,違背了“單一職責”的原則,所以需要將其可以獨立的模塊進行拆分,當然有可能由一個類拆分出多個類。當然,對類的細化也是為了減少代碼的重復性,以及提高代碼的復用性,便於代碼的維護。下方將會通過一個實例,對類進行提煉。
1.重構前的代碼
下方是我們將要進行重構的代碼段。在Person類中有三個字段,常量name表示該Employee的名字,officeAreaCode表示Employee所在辦公部門的區域代碼。然后就是Employee類的構造函數了。Employee類比較簡單。
2.使用Extract Class對Employee重構
接下來要做的就是使用Extract Class對Employee進行重構。因為上述Employee類設計的不好,因為Employee類可以再分。顯然可以將區域號和電話號提取成一個TelePhoneNubmer類,在Employee中調用TelePhoneNubmer類。這樣一來TelePhoneNubmer類就可以重復利用了,而且層次結構更為清晰。下方代碼段就是對上述代碼進行重構后的結果。具體如下所示:
四、Inline Class----類的內聯化
又到了“物極必反”的時候了。Extract Method與Inline Method職責相反,Extract Class當然也就職責相反的原則。那就是接下來要介紹的類的內聯化:Inline Class。如果過度使用Extract Class原則的話,會使得某些類過於簡單並且調用該簡單的類的地方極少。也就是說一個類根本不能稱為一個類,所以我們可以通過Inline Class將過度抽象出來的類放到其他類中。
關於Inline Class的示例在此就不做過多的贅述了,因為與Extract Class原則相反,將第三部分中的示例倒着過一遍即為類的內聯化的工作方式。
五、Hide Delegate----隱藏“委托關系”
隱藏類之間的“委托關系”這一原則用起來是非常不錯的,它可以簡化類調用委托者的方式。簡單的說就是講委托調用的鏈,封裝成相應的方法,使其隱藏掉具體的調用細節,從而簡化了調用方式。下方會根據具體事例和測試用例來介紹一下Hide Delegate。
1.重構前的案例
在下方代碼片段中有兩個類,這兩個類互為依賴關系。Department中有People,該People對應的就是經理人。還有一個字段就是chargeCode,對應的是部門代碼。而People類中有name--名字字段,department--所屬部門字段。在People對象中可以委托department對象來獲取經理的名字。
獲取People對象所在部門經理的名字的測試用例如下所示。在下方測試用例中創建了一個經理和一個員工,並為員工和經理綁定關系。zeluLi.department.manager.name就是委托department對象來調用經理的名字,這樣調用未免太長,所以有必要使用Hide Delegate原則對其進行優化。
2.使用Hide Delegate進行重構
使用Hide Delegate進行重構的方式是比較簡單的,就是在People中封裝一個方法,在方法中返回經理的對象即可,這樣就隱藏掉了委托關系。具體實現方式如下截圖所示:
添加上上面的函數后的調用方式如下:
Remove Middle Man(移除中間人)原則與Hide Delegate相反,就是沒有必要將委托人進行隱藏,所以就使用Remove Middle Man原則將上面我們封裝的獲取委托人的方法進行移除。關於Remove Middle Man的范例就不做過多的贅述了。
六、Introduce Foreign Method----引入外加函數
這一點在開發中用的還是比較多的,有時候你在不想或者不能修改原類的情況下想為該類添加新的方法。在這種情況下就會使用到Introduce Foreign Method。在Swift語言中,使用Introduce Foreign Method原則特別簡單,也就是在不改變類的情況下對類進行擴展也是特別簡單的。因為Swift語言以及OC中有延展的功能,所以非常對此非常好實現的。下方的代碼段就是對MyTest類使用extension為其擴展一個method2方法,具體如下所示。
今天的博客就先到這兒,后期還會繼續更新關於重構的博客。本篇博客中的代碼分享地址為:https://github.com/lizelu/CodeRefactoring-Swift