在《代碼重構(一):函數重構規則(Swift版)》和《代碼重構(二):類重構規則(Swift版)》 中詳細的介紹了函數與類的重構規則。本篇博客延續以前博客的風格,分享一下在Swift語言中是如何對數據進行重構的。對數據重構是頗有必要的,由於咱們 的程序主要是對數據進行處理。若是你的業務邏輯很是複雜,那麼對數據進行合理的處理是頗有必要的。對數據的組織形式以及操做進行重構,提升了代碼的可維護 性以及可擴展性。html
與函數重構與類重構相似,對數據結構的重構也是有必定的規則的。經過這些規則可使你更好的組織數據,讓你的應用程序更爲健壯。在本篇博客中將會結 合着Swift代碼實現的小實例來分析一下數據重構的規則,並討論一下什麼時候使用那些重構規則進行數據重構。仍是那句話「物極必反」呢,若是不恰當的使用重 構規則,或者過分的使用重構規則不但起不到重構的做用,有時還會起到副作用。廢話少說,進入今天數據重構的主題。git
一. Self Encapsulate Field (自封裝字段)github
"自封裝字段"理解起來比較簡單,一句話歸納:雖然字段對外是也隱藏的,可是仍是有必要爲其添加getter方法,在類的內部使用getter方法來代替self.field,該方式稱爲自封裝字段,本身封裝的字段,本身使用。 固然該重構規則不是必須執行的,由於若是你直接使用self來訪問類的屬性若是不妨礙你作擴展或者維護,那麼也是能夠的,畢竟直接訪問變量更爲容易閱讀。 各有各的好處,間接訪問字段的好處是使你的程序更爲模塊化,能夠更爲靈活的管理數據。好比在獲取值時,由於後期需求的變化,該獲取的字段須要作一些計算, 那麼間接訪問字段的方式就很容易解決這個問題,而直接訪問字段的方式就不是很好解決了。因此間接一下仍是好處多多的,不過直接訪問不影響你的應用程序的 話,也是無傷大雅的。編程
下方會經過一個實例來看一下間接訪問字段的好處。下方的IntRange類中的字段就沒有提供間接訪問的方法,在代碼中經過直接訪問的形式來使用的 字段。這種作法對當前的程序影響不大,可是如果提出需求了。要在high賦值後,在IntRange對類進行一個較爲複雜的修改。那麼對於下方代碼而言, 有兩種解決方案,就是在構函數中進行修改,在一個就是在使用self.high的地方進行修正,固然這兩種方法都不理想。最理性的方案是在相應字段的 getter方法修改。數組
下方截圖就是爲InRange類中相應的字段自封裝了getter和setter方法,並在使用self.字段的地方使用該自封裝的方法代替(構造函數中對字段的初始化除外,由於設置方法通常在對象建立完畢之後在調用,因此不能在建立對象時調用,固然Swift語言也不容許你在構造函數函數中調用設置方法)。下方紅框中的是咱們添加的自封裝方法,綠框中是對自封裝方法的使用,白框中是須要注意的一點,構造函數中不能使用該設置函數。安全
固然,只添加上上述自封裝字段後,優勢不明顯。固然子類CappedRange繼承了IntRange函數後,這種優勢就被顯示了出來。在子類中CappedRange的high須要與新添加的字段cap進行比較,取較大的值做爲區間的上限。在這種狀況下自封裝字段的優勢就被凸顯了出來。在子類中只須要對getHigh()函數進行從新,在重寫的方法中進行相應的計算便可。由於當在子類中調用inclued()方法時,在include()方法中調用的是子類的getHigh()方法。具體請看下方子類截圖:數據結構
二. Replace data Value with Object(以對象取代數據值)架構
「以對象取代數據值」說白了就是咱們常說的實體類,也就是Model類。Model的職責就將一些相關聯的數據組織在一塊兒來表示一個實體。 Model類比較簡單,通常只用於數據的存儲,其中有一些相關聯的字段,併爲這些相關聯的字段添加getter/和setter方法。下方是一個 Person的數據模型,咱們命名爲PersonModel,其中有三個表示Person屬性的字段name、birthday、sender。而後提供 了一個構造器以及各個屬性對應的getter和setter方法。具體請看下方代碼所示:app
3、Change Value to Reference (將值對象改變成引用對象)框架
在介紹「將值對象改變成引用對象」之 前,咱們先去了解一下值對象和引用對象的區別。先說一下值對象,好比兩個相等的數值,存入了兩個值對象中,這兩個值對象在內存中分別佔有兩塊不一樣的區域, 因此改變其中一個值不會引發另外一個值得變化。而引用對象正好相反,一個內存區域被多個引用指針所引用,這些引用指針即爲引用對象,由於多個指針指向同一塊 內存地址,因此不管你改變哪個指針中的值,其餘引用對象的值也會跟着變化。
基於值對象和引用對象的特色,咱們有時 候根據程序的上下文和需求須要將一些值類型改變成引用類型。由於有時候須要一些類的一些對象在應用程序中惟一。這和單例模式又有一些區別,單例就是一個類 只能生成一個對象,而「將值對象改變成引用對象」面臨的就是類能夠建立多個對象,可是這多個對象在程序中是惟一的,而且在某一個引用點修改對象中的屬性 時,其餘引用點的對象值也會隨之改變。下方就經過一個訂單和用戶的關係來觀察一下這個規則。
1. 值引用的實例
(1) 首先咱們須要建立一個消費者也就是Customer類。Customer類比較簡單,其實就是一個數據實體類。其中有name和idCard屬性並對應着getter/setter方法,具體代碼以下所示:
(2)、緊接着咱們須要建立一個訂單類,在訂單建立時咱們須要爲該訂單關聯一個Customer(固然這爲了簡化實例,咱們省略了Order中的其餘字段)。該Order類的代碼也是比較簡單的在此就不作過的的贅述了。不過有一點須要注意的是爲了測試,咱們將customer設計成值類型,也就是每一個Order中的customer都會佔用不一樣的內存空間,這也就是值類型的特色之一。
(3).建立完Order與 Customer類後,緊接着咱們要建立測試用例了。並經過測試用例來發現問題,並在重構時對該問題進行解決。在測試用例中咱們建立了三個訂單,爲每一個訂 單關聯一個Customer。從測試用例中能夠看出,關聯的消費者數據爲同一我的,可是這一我的在內存中佔用了不一樣的存儲空間,若是一個訂單中的用戶信息 進行了更改,那麼其餘訂單中的用戶信息是不會更新的。若是建立完用戶後,信息不可更改,雖然浪費點存儲空間,可是使用值類型是沒用問題的。一旦某個訂單修 改了用戶名稱,那麼就會出現數據不一樣步的問題。
2.將Order中Customer改成引用類型(從新設計Order類)
由於在Swift語言中類自己就是引用類型,因此在設計Order時,咱們值須要將其中的customer字段改爲引用外部的Customer類的 對象便可。這樣一來多個訂單能夠引用同一個用戶了,並且一個訂單對用戶信息修改後,其餘訂單的用戶信息也會隨之改變。要實現這一點須要對Order的構造 函數和customer的設置函數進行修改,將在Order內部建立Customer對象的方式改變成將外部Customer對象的引用賦值給Order 中的custom對象。說白了,修改後的Order中的customer對象就是外部對象的一個引用。這種方法能夠將值對象改變成引用對象
上面這種作法能夠將值對象改變成引用對象,可是代價就是改變Order建立的方式。上面代碼修改完了,那麼咱們的測試用例也就做廢了,由於Order的建立方式進行了修改,須要外部傳入一個Customer對象,下方截圖就是咱們修改後的測試用例。(若是你是在你的工程中這麼去將值對象修改引用對象的,不建議這麼作,下面會給出比較好的解決方案)。
3.從根本上進行重構
上面代碼的修改不能稱爲代碼的重構,由於其改變的是不只僅是模塊內部的結構,並且修改了模塊的調用方式。也就是說裏外都被修改了,這與咱們重構所提 倡的「改變模塊內部結構,而不改變對外調用方式」所相悖。因此在代碼重構時不要這麼作了,由於上面的這種作法的成本會很高,而且出現BUG的概率也會提 高。由於每一個使用訂單的地方都會建立一個Customer的類來支持訂單的建立,那麼問題來了,若是同一用戶在不一樣地方建立訂單怎麼辦?因此上面的作法仍是有問題的,終歸是治標不治本。因此咱們要從根本上來解決這個問題。由於該問題是由於Customer數據不一樣步引發的,因此咱們還得從Customer來下手。
該部分的重構,在第一部分的基礎上作起。咱們本次重構的目標就是「不改變對外調用方式,但能保持每一個用戶是惟一的」。好接下來就開始咱們真正的重構工做。在本次重構中,依照重構的規則,咱們不會去修改咱們的測試用例,這一點很重要。
(1)從根本解決問題,首先咱們對Customer進行重構。在Customer中添加了一個靜態的私有變量customers, 該靜態私有變量是字典類型。其中存儲的就是每次建立的消費者信息。在字典中每一個消費者的key爲消費者獨一無二的身份證信息(idCard)。在添加完上 述變量後,咱們須要爲建立一個工廠方法createCustomer() 在工廠方法中,若是當前傳入的用戶信息未被存入到字典中,咱們就對其進行建立存入字典,並返回該用戶信息。若是傳入的用戶已經被建立過,那麼就從字典中直接取出用戶對象並返回。具體作法以下所示。
(2)、對Customer類修改完畢後,咱們須要在Order中經過Customer的工廠方法來獲取Customer類的實例,這樣就能保證 Order中的customer對象也是引用對象了。不過此時的引用對象是從Customer中獲取的,而不是外部傳過來的。下方是Order類中對工廠 方法的調用,這樣作的好處就是,咱們只對模塊的內部進行了修改,而測試用例無需修改。
(3)、對這次重進行測試,咱們任然使用第一部分使用的測試用例。也就是說該模塊對外的接口是沒有變化的,下方就是對重構後的代碼的測試結果。由 結果能夠看出,在不一樣訂單中的用戶,只要是信息一致,那麼其內存地址是一致的。也就是通過重構,咱們將原來的值對象改爲了引用對象。
4、Change Reference to Value(將引用對象改成值對象)
將引用對象改成值對象,該重構規則正好與上面相反。在一 些狀況下使用值對象更爲簡單,更易管理,但前提是該值對象很小而且不會被改變。在這種狀況下你就沒有必要使用引用對象了。從上面的示例來看,使用引用對象 實現起來仍是較爲複雜的。仍是那句話,若是你的對象很是小,並且在建立後其中的數據不會被改變,若是須要改變就必須在建立一個新的對象來替換原來的對象。 在這種狀況下使用值對象是徹底能夠的。在此就不作過多的贅述了。
不過在使用值對象時,你最好爲值對象提供一個重載的相等運算符用來比較值對象中的值。也就是說只要是值對象中的每一個屬性的值都相同,那麼這兩個值對象就相等。至於如何對「==」 運算符進行重載就不作過多的贅述了,由於該知識點不是本篇博客的重點。
5、Replace Array or Dictionary with Object(以對象取代數組或字典)
這一點呢和本篇博客的第二部分實際上是一 個。就是當你使用數組或者字典來組織數據,這些數據組合起來表明必定的意義,這是最好將其定義成一個實體類。仍是那句話,定義成實體類後,數據更易管理, 便於後期需求的迭代。下方代碼段就是講相應的字典和數組封裝成一個實體類,由於確實比較簡單,在此就不作過多的贅述了。具體請參加下方代碼段。
6、Duplicate Observed Data(複製「被監測數據」)
這 一部分是比較重要的部分,也是在作UI開發時常常遇到的部分。用大白話將就是你的業務邏輯與GUI柔和在了一塊兒,由於UI做爲數據的入口,因此在寫程序 時,咱們就很容易將數據處理的方式與UI寫在一塊兒。這樣作是很是很差的,不利於代碼的維護,也不利於代碼的可讀性。隨着需求不斷的迭代,版本不斷的更 新,UI與業務邏輯融合的代碼會變得很是難於維護。因此咱們仍是有必要將於UI無關的代碼從UI中進行分離,關於如何進行分層宏觀的作法請參加以前發佈的 博客《iOS開發之淺談MVVM的架構設計與團隊協做》。
今天博客中的該部分是分層的微觀的東西,也就是具體如何將業務邏輯從GUI中進行剝離。因此在接下來的實例中是和UI實現有關的,會根據一個比較簡單的Demo來一步步的將UI中的業務邏輯進行分離。進入該部分的主題。複製「被監測數據」 簡單的說,就是將UI提供的數據複製一份到咱們的業務邏輯層,而後與UI相應的數據進行關聯,UI數據變化,被複制的業務邏輯中的數據也會隨之變化。這一 點也就是所謂的"響應式編程"吧,關於響應式編程,iOS開發中會常常用到ReactiveCocoa這個框架,關於ReactiveCocoa的內容, 請參見以前的博客《iOS開發之ReactiveCocoa下的MVVM》。今天的示例中,使用了一個比較簡單的方式來同步這些數據,使用了"事件監聽機制"。下方就建立一個比較簡單的Demo。
1.建立示例
要建立的示例比較簡單,在UI方面,只有三個輸入框用來接收加數與被加數,以及用來顯示兩數之和。而後使用兩個UILabel來顯示+號與=號。咱們要實現的功能就是改變其中一個加數與被加數時,自動計算兩個數的和並顯示。
要實現上述功能的代碼也是比較簡單的,總共沒有幾行,下方這個類就是實現該功能的所有代碼。代碼的核心功能就是「獲取加數與被加數的和,而後在加數與被加數的值有一個改變時,就會計算二者之和,並將和賦值給最後一個輸入框進行顯示」。具體代碼以下所示。
1 class AddViewControllerBack: UIViewController { 2 3 //三個輸入框對應的字段 4 @IBOutlet var firstNumberTextField: UITextField! 5 @IBOutlet var secondNumberTextField: UITextField! 6 @IBOutlet var resultTextField: UITextField! 7 8 override func viewDidLoad() { 9 super.viewDidLoad() 10 } 11 12 //獲取第一個輸入框的值 13 func getFirstNumber() -> String { 14 return firstNumberTextField.text! 15 } 16 17 //獲取第二個輸入框的值 18 func getSecondNumber() -> String { 19 return secondNumberTextField.text! 20 } 21 22 //加數與被加數中的值改變時會調用的方法 23 @IBAction func textFieldChange(sender: AnyObject) { 24 self.resultTextField.text = calculate(getFirstNumber(), second: getSecondNumber()) 25 } 26 27 28 //計算兩個數的值 29 func calculate(first: String, second: String) -> String { 30 return String(stringToInt(first) + stringToInt(second)) 31 } 32 33 //將字符串安全的轉變成整數的函數 34 func stringToInt(str: String) -> Int { 35 guard let result = Int(str) else { 36 return 0 37 } 38 return result 39 } 40 }
2.對上述代碼進行分析並重構
由於代碼比較簡單,因此很容易進行分析。在上述UI代碼中,咱們很清楚的看到後兩個函數,也就是calculate()與stringToInt()函數是數據處理的部分,只依賴於數據,與UI關係不是很大,因此咱們可使用複製「被監測數據」規則將該段業務邏輯代碼進行提取重構。重構後UI以及UI對外的工做方式不變。
下方的Calculate類就是咱們提 取的數據業務類,負責處理數據。在該類中咱們建立了三個屬性來與UI中的輸入框進行對應,這也就是所說的複製「被監測的數據」。由於和也就是 resultNumber是由firstNumber和SecondNumber計算而來的,因此咱們就把resultNumber定義成了計算屬性,而 firstNumber和secondNumber爲存儲屬性。併爲存儲屬性提供setter方法。在Calculate類的構造函數中,咱們爲兩個值指定了初始化數據也就是「0」。最下方的那兩個函數就是咱們從UI中直接拷貝過來的數據,一點沒有修改,也是能夠工做的,由於這部分代碼只依賴於數據,而不依賴於UI。
建立爲相應的業務邏輯處理類並提取完業務邏輯後,咱們須要將業務邏輯中的數據,也就是複製過來的數據與UI中的數據提供者進行綁定,並返回計算結 果。下方紅框中就是咱們要修改的部分,在UI中咱們刪除掉處理業務數據的代碼,而後建立也給Calculate對象,並在相應的事件監聽的方法中更新 Calculate對象中的數據。以下所示
7、Change Unidirectional Association to Bidirectional(將單向關聯改成雙向關聯)
要介紹本部分呢,我想引用本篇博文中第(三)部分是實例。由於在第三部分的實例中Customer與Order的關係是單向關聯的,也就是說 Order引用了Customer, 而Customer沒有引用Order。換句話說,咱們知道這個訂單是誰的,但你不知道只經過用戶你是沒法知道他有多少訂單的。爲了只經過用戶咱們就能知 道該用戶有多少訂單,那麼咱們須要使用到「將單向關聯改成雙向關聯」這條規則。
1. 在Customer類中添加上指向Order類的鏈
由於Customer沒有指向Order類的鏈,因此咱們不能獲取到該用戶有多少訂單,如今咱們就要添加上這條鏈。將單向關聯改成雙向關聯,具體作法是在Customer中添加一個數組,該數組中存儲的就是該用戶所擁有的訂單。這個數組就是咱們添加的鏈。數組以下:
1 //添加與Order關聯的鏈,一個用戶有多個訂單 2 private var orders:Array<Order> = []
在Customer中值只添加數組也是不行的呢,根據以前提到的重構規則,咱們要爲數組封裝相應的操做方法的,下方就是咱們要在Customer中添加的操做數組的方法。具體代碼以下所示:
1 //====================添加================== 2 func addOrder(order: Order) { 3 self.orders.append(order) 4 } 5 6 func getOrders() -> Array<Order> { 7 return self.orders 8 }
在Order類關聯Customer時,創建Customer到Order的關聯。也就是將當前訂單添加進該用戶對應的訂單數組中,具體作法以下:
與之對應的規則是Change Bidirectional Association to Unidirectional(將雙向關聯改成單向關聯),就是根據特定需求刪去一個鏈。就是說,原來須要雙向鏈,可現在因爲需求變動單向關聯便可,那麼你就應該將雙向關聯改成單向關聯。
8、Replace Magic Number with Synbolic Constant(以字面常量取代魔法數)
這一點說白了就是不要在你的應用程序中 直接出現字數值。這一點很好理解,在使用字面數值時,咱們要使用定義好的常量來定義。由於這樣更易於維護,若是同一個字面數值寫的處處都是,維護起來及其 困難。當使用字面常量時維護起來就容易許多。該規則比較容易理解,在此不作過多的贅述。看下方實例便可。對於下方的實例而言,若是在版本迭代中所需的PI 的精度有所改變,那麼對於替換後的程序而言,咱們只需修改這個常量的值便可。
1 func test(height: Double) -> Double { 2 return 3.141592654 * height 3 } 4 5 //替換 6 let PI = 3.141592654 7 func test1(height: Double) -> Double { 8 return PI * height 9 }
9、Encapsulate Field(封裝字段)
當你的類中有對外開放字段時,最好將其進行封裝,不要直接使用對象來訪問該字段,該優缺點與上述的「自封裝字段」的優缺點相似。由於直接訪問類的字段,會下降程序的模塊化,不利於程序的擴充和功能的添加。再者封裝是面向對象的特徵之一,因此咱們須要將字段變成私有的,而後對外提供相應的setter和getter方法。具體作法以下所示。
1 //重構前 2 class Person { 3 var name: String = "" 4 5 init(name: String) { 6 self.name = name 7 } 8 } 9 10 //重構後 11 class Person { 12 private var name: String = "" 13 14 init(name: String) { 15 self.name = name 16 } 17 18 func getName() -> String { 19 return name 20 } 21 22 func setName(name: String) { 23 self.name = "China:" + name 24 } 25 }
10、Encapsulate Collection(封裝集合)
「封裝集合」這一重構規則應該來講並不難理解。當你的類中有集合時,爲了對該集合進行封裝,你須要爲集合建立相應的操做方法,例如增刪改查等等。下方就經過一個不封裝集合的實例,看一下缺點。而後將其重構。關於「封裝集合」具體的細節參見下方實例。
1.未封裝集合的實例
下方咱們先建立一個圖書館圖書類,爲了簡化示例,該圖書類只有一個書名。下方代碼段就是這個圖書類,以下所示:
class LibraryBook { private var name: String init(name: String) { self.name = name } func getName() -> String { return self.name } }
緊接着要建立一個借書者,借書者中有兩個字段,一個是借書者的名字,另外一個是所借書籍的數組。在Lender中咱們沒有爲lendBooks數組封裝相應的方法,只爲其提供了getter/setter方法,具體代碼以下所示。
1 class Lender { 2 private var name: String 3 private var lendBooks: Array<LibraryBook> = [] 4 5 init(name: String) { 6 self.name = name 7 } 8 9 func getName() -> String { 10 return self.name 11 } 12 13 func setLendBooks(books: Array<LibraryBook>) { 14 self.lendBooks = books 15 } 16 17 func getLendBooks() -> Array<LibraryBook> { 18 return self.lendBooks 19 } 20 }
緊接着咱們要建立一個測試用例,觀察這兩個類的使用方式。由下面程序的註釋可知,首先咱們須要建立一個books的數組,該數組就像一個籃子似的, 它能夠存儲咱們要借的書籍。讓後將建立的書籍添加到該數組中,最後將books賦值給借書人中的lendBooks。若是要對書籍進行修改,那麼只有先獲 取借書人的lendBooks, 而後進行修改,最後再將修改後的值賦值回去。
1 //先建立一個書籍數組 2 var books: Array<LibraryBook> = [] 3 //添加要借的書籍 4 books.append(LibraryBook(name: "《雪碧加鹽》")) 5 books.append(LibraryBook(name: "《格林童話》")) 6 books.append(LibraryBook(name: "《智慧意林》")) 7 8 //建立借書人 9 let lender: Lender = Lender(name: "ZeluLi") 10 lender.setLendBooks(books) 11 12 //獲取所借書籍 13 var myBooks = lender.getLendBooks() 14 15 //對書籍數組修改後再賦值回去 16 myBooks.removeFirst() 17 lender.setLendBooks(myBooks)
2.爲上面的Lender類添加相應的集合操做的方法
由上面的測試用例能夠看出,Lender類封裝的很差。由於其使用方式以及調用流程太麻煩,因此咱們得從新對其進行封裝。因此就會用到 「Encapsulate Collection」原則。下面咱們就會爲Lender添加上相應的集合操做的方法。說白了,就是講上面測試用例作的一部分工做放到Lender類中。 下方是爲Lender添加的對lendBooks相應的操做方法。下方代碼中的Lender類與上面的Lender類中的lendBooks不一樣,咱們使 用了另外一個集合類型,也就是字典,而字典的key就是書名,字典的值就是書的對象。具體代碼以下所示:
通過上面這樣一封裝的話,使用起來就更爲合理與順手了。用大白話講,就是好用。下方是咱們從新封裝後的測試用例,簡單了很多,並且組織也更爲合理。具體請看下方代碼段:
11、Replace Subclass with Fields(以字段取代子類)
什麼叫「以字段取代子類」呢?就是當你的各個子類中惟一的差異只在「返回常量數據」的函數上。當遇到這種狀況時,你就能夠將這個返回的數據放到父類 中,並在父類中建立相應的工廠方法,而後將子類刪除便可。直接這樣說也許有些抽象,接下來,咱們會經過一個小的Demo來看一下這個規則具體如何應用。1.建立多個子類,並每一個子類只有一個函數的返回值不一樣
接下來咱們就要建立重構前的代碼了。首先咱們建立一個PersonType協議(也就是一個抽象類),該協議有兩個方法,一個是isMale(), 若是是子類是男性就返回true,若是子類是女性就返回false。還有一個是getCode()函數,若是子類是男性就返回「M」,若是是子類是女性就 返回「F」。 這兩個子類的差異就在於各個函數返回的值不一樣。下方是PersonType的具體代碼。
1 protocol PersonType { 2 func isMale() -> Bool 3 func getCode() -> String 4 }
而後咱們基於PersonType建立兩個子類,一個是Male表示男性,一個是Female表示女性。具體代碼以下:
1 class Male: PersonType { 2 func isMale() -> Bool { 3 return true 4 } 5 6 func getCode() -> String { 7 return SenderCode.Male.rawValue 8 } 9 } 10 11 class Female: PersonType { 12 func isMale() -> Bool { 13 return false 14 } 15 16 func getCode() -> String { 17 return SenderCode.Female.rawValue 18 } 19 }
上述代碼的SenderCode是咱們自定義的枚舉類型,用來表示"M"與「F」,枚舉的代碼以下:
1 enum SenderCode: String { 2 case Male = "M" 3 case Female = "F" 4 }
2.以字段取代子類
從上面的代碼容易看出,Male與Female類實現相同的接口,但接口函數在兩個類中的返回值是不一樣的。這時候咱們就可使用「以字段取代子類」的方式來進行重構,下方截圖就是重構後的代碼片斷。
下方代碼中,將PersonType聲明瞭一個類,在類中添加了兩個字段,一個是isMale,另外一個是code,這兩個字段剛好是上述兩個子類函 數中返回的不一樣值。這也就是使用字段來取代子類,由於有了這兩個字段,咱們就能夠不用去建立子類了,而是直接在PersonType中經過工廠方法根據不 同的性別分別給這兩個新加的字段賦上不一樣的值。具體作法以下。
通過上面這段代碼重構後,咱們就能夠調用PersonType的不一樣的工廠方法來建立不一樣的性別了。測試用例以下所示:
OK~今天博客的內容也夠多的了,那就先到這兒。關於重構的其餘規則,還會在後期的博客中繼續更新。
今天博客中是示例在GitHub上的分享地址爲:https://github.com/lizelu/CodeRefactoring-Swift