重构-改善既有代码的设计:重新组织数据的16种方法(六)
1.Self Encapsulate Field 自封裝字段
間接訪問(wèn)類的屬性:你直接訪問(wèn)一個(gè)字段,但與字段之間的耦合關(guān)系逐漸變得笨拙。為這個(gè)字段建立取值/設(shè)值函數(shù),并且只以這些函數(shù)來(lái)訪問(wèn)字段。
? ? ? ?間接訪問(wèn)變量的好處是,子類可以通過(guò)覆寫(xiě)一個(gè)函數(shù)而改變獲取數(shù)據(jù)的途徑;它還支持更靈活的數(shù)據(jù)管理方式,例如延遲初始化。
???????如果你想訪問(wèn)超類中的一個(gè)字段,卻又想子類中將對(duì)這個(gè)變量的訪問(wèn)改為一個(gè)計(jì)算后的值,這就是使用Self Encapsulate Field (自封裝字段)的時(shí)候。“字段自我封裝”只是第一步。完成自我封裝后,你可以在子類中根據(jù)自己的需要隨意覆寫(xiě)取值/設(shè)值函數(shù)。
2.?Replace Data Value with Object 對(duì)象取代數(shù)據(jù)值
聚合改為組合:你有一個(gè)數(shù)據(jù)項(xiàng),需要與其他數(shù)據(jù)和行為一起使用才有意義。將數(shù)據(jù)項(xiàng)變成對(duì)象。
3.Change value to Reference 將值對(duì)象改為引用對(duì)象
組合改為重?cái)?shù)性關(guān)聯(lián)(另一個(gè)類的一個(gè)對(duì)象與一個(gè)或多個(gè)該類對(duì)象有關(guān)系?Customer類對(duì)象可以包含多個(gè)order類的對(duì)象。):從一個(gè)類中衍生出許多彼此相等的實(shí)例,希望將它們替換為一個(gè)對(duì)象。將這個(gè)值對(duì)象變成引用對(duì)象。(Customer是一個(gè)實(shí)值對(duì)象,就算多份訂單屬于一個(gè)客戶每個(gè)Order對(duì)象還是擁有各自的Customer對(duì)象。重構(gòu)的結(jié)果是所有的訂單對(duì)象共享一個(gè)客戶名稱。也就是,一個(gè)客戶對(duì)象只對(duì)應(yīng)一個(gè)客戶名稱。)
? ? 可以將對(duì)象分成兩類:reference object(引用對(duì)象)和value object(實(shí)值對(duì)象)。
有一個(gè)實(shí)值對(duì)象,在其中保存了少量的不可修改的數(shù)據(jù)。你需要給這個(gè)對(duì)象加入一些可修改的數(shù)據(jù),并確保對(duì)任何一個(gè)對(duì)象的修改都能影響到所有引用此對(duì)象的地方。這是后就需要將value object(實(shí)值對(duì)象)變成一個(gè)reference object(引用對(duì)象)。
? ? ? ? 在許多系統(tǒng)中,都可以對(duì)對(duì)象做一個(gè)有用的分類:引用對(duì)象和值對(duì)象。要在引用對(duì)象和值對(duì)象之間做選擇有時(shí)并不容易。有時(shí)候,你會(huì)從一個(gè)簡(jiǎn)單的值對(duì)象開(kāi)始,在其中保存少量不可修改的數(shù)據(jù)。而后,你可能會(huì)希望給這個(gè)對(duì)象加入一些可修改數(shù)據(jù),并確保對(duì)任何一個(gè)對(duì)象的修改都能影響到所引用此對(duì)象的地方。這時(shí)候你就需要將這個(gè)對(duì)象變成引用對(duì)象。
4.?Change Reference to Value 將引用對(duì)象改為值對(duì)象
你有一個(gè)引用對(duì)象,很小且不可變,而且不易管理。 將它變成一個(gè)值對(duì)象。
?
? ? ? ?要在引用對(duì)象和值對(duì)象之間做選擇,有時(shí)并不容易。做出選擇后,你常會(huì)需要一條回頭路,
???????如果引用對(duì)象開(kāi)始變得難以使用,也許就應(yīng)該將它改為值對(duì)象。引用對(duì)象必須被某種方式控制,你總是必須向其控制者請(qǐng)求適當(dāng)?shù)囊脤?duì)象。它們可能造成內(nèi)存區(qū)域之間錯(cuò)綜復(fù)雜的關(guān)聯(lián)。在分布式和并發(fā)系統(tǒng)中,不可變的值對(duì)象特別有用,因?yàn)槟銦o(wú)需考慮它們的同步問(wèn)題。
???????值對(duì)象有一個(gè)非常重要的特征:它們應(yīng)該是不可變的。無(wú)論何時(shí),只有你調(diào)用同一對(duì)象的同一個(gè)查詢函數(shù),都應(yīng)該得到同樣結(jié)果。如果保證了這一點(diǎn),就可以放心地以多個(gè)對(duì)象表示同一個(gè)事物。如果值對(duì)象是可變的,你就必須確保某個(gè)對(duì)象的修改會(huì)自動(dòng)更新其他“代表相同事物”的對(duì)象。與其如此還不如把它變成引用對(duì)象。
???????澄清下“不可變”(immutable)的意思:如果你以money類表示“錢(qián)’的概念,其中有”幣種“和”金額“2條信息。那么Money對(duì)象通常是一個(gè)不可變的值對(duì)象。這并非意味你的薪資不能改變,而是意味:如果要改變你的薪資,就需要使用另一個(gè)Money對(duì)象來(lái)取代現(xiàn)有的Money對(duì)象,而不是在現(xiàn)有的Money對(duì)象上修改。你和Money對(duì)象之間的關(guān)系可以改變,但Money對(duì)象自身不能改變。
5.Replace Array with Object 以對(duì)象取代數(shù)組
你有一個(gè)數(shù)組,其中的元素各自代表不同的東西。以對(duì)象替換數(shù)組,對(duì)于數(shù)組中的每個(gè)元素,以一個(gè)字段來(lái)表示。
數(shù)組時(shí)一種常見(jiàn)的用以組織數(shù)據(jù)的結(jié)構(gòu)。不過(guò),它們應(yīng)該只用于“以某種順序容納一組相似對(duì)象”。有時(shí)候你會(huì)發(fā)現(xiàn),一個(gè)數(shù)組容納了多種不同對(duì)象,這會(huì)給用戶帶來(lái)麻煩,因?yàn)樗麄兒茈y記住像“數(shù)組的第一個(gè)元素是人名”這樣的約定。對(duì)象就不同了,你可以運(yùn)用字段名和函數(shù)名來(lái)傳達(dá)這樣的信息,因此你無(wú)需死記它,也無(wú)需依賴注釋。而且如果使用對(duì)象,你還可以將信息封裝起來(lái)。并使用 Move Method (搬移函數(shù))為它加上相關(guān)行為。
6.?Duplicate Observed data 復(fù)制被監(jiān)視數(shù)據(jù)
你有一些領(lǐng)域數(shù)據(jù)置身于GUI控件中,而領(lǐng)域函數(shù)需要訪問(wèn)這些數(shù)據(jù)。將該數(shù)據(jù)復(fù)制到一個(gè)領(lǐng)域?qū)ο笾小=⒁粋€(gè)Observer模式,用以同步領(lǐng)域?qū)ο蠛虶UI對(duì)象內(nèi)的重復(fù)數(shù)據(jù)。
一個(gè)分層良好的系統(tǒng),應(yīng)該將處理用戶界面和處理業(yè)務(wù)邏輯的代碼分開(kāi)。之所以這樣做,原因有以下幾點(diǎn):1)你可能需要使用不同的用戶界面來(lái)表現(xiàn)相同的業(yè)務(wù)邏輯,如果同時(shí)承擔(dān)2種責(zé)任,用戶界面會(huì)變得過(guò)分復(fù)雜;2)與GUI隔離后,領(lǐng)域?qū)ο蟮木S護(hù)和演化都會(huì)更容易,你甚至可以讓不同的開(kāi)發(fā)者負(fù)責(zé)不同部分的開(kāi)發(fā)。
???????盡管可以輕松地將“行為”劃分到不同部位,“數(shù)據(jù)”卻往往不能如此。同一項(xiàng)數(shù)據(jù)可能既需要內(nèi)嵌于GUI控件,也需要保存于領(lǐng)域模型里。自從MVC模式出現(xiàn)后,用戶界面框架都使用多層系統(tǒng)來(lái)提供某種機(jī)制,使你不但可以提供這類數(shù)據(jù),并保持它們同步。
???????如果你遇到的代碼是以2層方式開(kāi)發(fā),業(yè)務(wù)邏輯被內(nèi)嵌于用戶界面之中,你就有必要將行為分離出來(lái)。其中的主要工作就是函數(shù)的分解和搬移。但數(shù)據(jù)就不同了;你不能僅僅只是移動(dòng)數(shù)據(jù),必須將它復(fù)制到新的對(duì)象中,并提供相應(yīng)的同步機(jī)制。
?
7.Change Unidirection Association to Bidirectional 將單向關(guān)聯(lián)改為雙向關(guān)聯(lián)
兩個(gè)類都需要使用對(duì)方特性,但其間只有一條單向連接。添加一個(gè)反向指針,并使修改函數(shù)能夠同時(shí)更新2條連接。
開(kāi)發(fā)初期,你可能會(huì)在2個(gè)類之間建立1條單向連接,使其中一個(gè)類可以使用另一個(gè)類。隨著時(shí)間推移,你可能發(fā)現(xiàn)被引用類需要得到其引用者以便進(jìn)行某些處理。也就是說(shuō)它需要一個(gè)反向指針。但指針是一種單向連接,你不可能反向操作它。通常你可以繞道而行,雖然會(huì)耗費(fèi)一些計(jì)算時(shí)間,成本還算合理,然后你可以在被引用類中建立一個(gè)函數(shù)專門(mén)負(fù)責(zé)此行為。但是,有時(shí)候想繞過(guò)這個(gè)問(wèn)題并不容易,此時(shí)就需要建立雙向引用關(guān)系,或稱為反向指針。如果使用不當(dāng),反向指針和很容易造成混亂;但只要你習(xí)慣了這種手法,它們其實(shí)并不太復(fù)雜。
???????“反向指針”手法有點(diǎn)棘手,所以在你能夠自如運(yùn)用之前,應(yīng)該有相應(yīng)的測(cè)試。通常不用花心思去測(cè)試訪問(wèn)函數(shù),因?yàn)槠胀ㄔL問(wèn)函數(shù)的風(fēng)險(xiǎn)沒(méi)有高到需要測(cè)試的地步,但本重構(gòu)要求測(cè)試訪問(wèn)函數(shù),所以它是極少數(shù)需要添加測(cè)試的重構(gòu)手法之一。
???????本重構(gòu)運(yùn)用反向指針實(shí)現(xiàn)雙向關(guān)聯(lián),其他技術(shù)需要其他重構(gòu)手法。
8.Change Bidirectional ?Association to Unidirection將雙向關(guān)聯(lián)改為單向關(guān)聯(lián)
兩個(gè)類之間有雙向關(guān)聯(lián),但其中一個(gè)類如今不再需要另一個(gè)類的特性。去除不必要的關(guān)聯(lián)。雙向關(guān)聯(lián)很有用,但你必須為它付出代價(jià),那就是維護(hù)雙向連接、確保對(duì)象被正確創(chuàng)建和刪除而增加的復(fù)雜度。而且,由于很多程序員并不習(xí)慣使用雙向關(guān)聯(lián),它往往成為錯(cuò)誤之源。
???????大量的雙向連接也很容易造成“僵尸對(duì)象”:某個(gè)對(duì)象本來(lái)應(yīng)該死亡了,卻仍然保留在系統(tǒng)中,因?yàn)閷?duì)它的引用沒(méi)有完全清除。
???????此外,雙向關(guān)聯(lián)也迫使2個(gè)類之間有了依賴:對(duì)其中任一個(gè)類的任何修改,都可能引發(fā)另一個(gè)類的變化。如果這2個(gè)類位于不同的程序集,這種依賴就是程序集之間的相依。過(guò)多的跨程序集依賴會(huì)造就緊耦合的系統(tǒng),使得任何一點(diǎn)小小改動(dòng)就可能造成許多無(wú)法預(yù)知的后果。
???????只有在真正需要雙向關(guān)聯(lián)的時(shí)候,才該使用它。如果發(fā)現(xiàn)雙向關(guān)聯(lián)不再有存在價(jià)值,就應(yīng)該去掉不必要的一條關(guān)聯(lián)。
9.Replace Magic Number with Symbolic Constant字面常量取代魔法數(shù)
你有一個(gè)字面數(shù)值,帶有特別含義。創(chuàng)建一個(gè)常量,根據(jù)其意義為它命名,并將上述的字面數(shù)值替換為這個(gè)常量。? ? ? ? 在計(jì)算科學(xué)中,魔法數(shù)是歷史悠久的不良現(xiàn)象之一。所謂魔法數(shù)是指擁有特殊意義,卻又不能明確表現(xiàn)出這種意義的數(shù)字。如果你需要在不同的地點(diǎn)引用同一個(gè)邏輯數(shù),魔法數(shù)會(huì)讓你煩惱不已,因?yàn)橐坏┻@些數(shù)發(fā)生變化,你就必須在程序中找到所有魔法數(shù),并將它們?nèi)啃薷囊槐椤>退隳悴恍枰薷?#xff0c;要準(zhǔn)確指出每個(gè)魔法數(shù)的用途,也會(huì)讓你頗費(fèi)腦筋。
???????許多語(yǔ)言都允許聲明常量。常量不會(huì)造成任何性能開(kāi)銷,卻可以大大提高代碼的可讀性。
???????進(jìn)行本項(xiàng)重構(gòu)之前,你應(yīng)該先尋找其他替代方案。你應(yīng)該觀察魔法數(shù)任何被使用,而后你往往會(huì)發(fā)現(xiàn)一種更好的使用方式。如果這個(gè)魔法數(shù)是個(gè)類型碼,請(qǐng)考慮使用 Replace Type Code with Class (以類取代類型碼);如果這個(gè)魔法數(shù)代表一個(gè)數(shù)組的長(zhǎng)度,請(qǐng)?jiān)诒闅v數(shù)組時(shí),改用數(shù)組.length。
10.Encapsulate Field 封裝字段
你的類中存在一個(gè)public字段。將它聲明為private,并且提供相應(yīng)的訪問(wèn)函數(shù)。面向?qū)ο蟮氖滓瓌t之一就是封裝,或者稱為“數(shù)據(jù)隱藏”。按此原則,你絕不應(yīng)該將數(shù)據(jù)聲明為public,否則其他對(duì)象就有可能訪問(wèn)甚至修改這項(xiàng)數(shù)據(jù),而擁有該數(shù)據(jù)的對(duì)象卻毫無(wú)察覺(jué)。于是,數(shù)據(jù)和行為就被分開(kāi)了。
???????數(shù)據(jù)聲明為public被看做是一種不好的做法,因?yàn)檫@樣會(huì)降低程序的模塊化程度。數(shù)據(jù)和使用該數(shù)據(jù)的行為如果集中在一起,一旦情況發(fā)生變化,代碼的修改就會(huì)比較簡(jiǎn)單,因?yàn)樾枰薷牡拇a都集中于同一塊地方,而不是星羅棋布地散落在整個(gè)程序中。
?????? Encapsulated Field?(封裝字段)是封裝過(guò)程的第一步,通過(guò)這項(xiàng)重構(gòu)手法,你可以將數(shù)據(jù)隱藏起來(lái),并通過(guò)相應(yīng)的訪問(wèn)函數(shù)。但它畢竟只是第一步。如果一個(gè)類除了訪問(wèn)函數(shù)外不能提供其他行為,它終究只是一個(gè)啞巴類。這樣的;類并不能享受對(duì)象技術(shù)帶來(lái)的好處。實(shí)施Encapsulated Field (封裝字段)之后,嘗試尋找用到新建訪問(wèn)函數(shù)的代碼,看看是否可以通過(guò)簡(jiǎn)單的 Move Method(搬移函數(shù))將它們移到新對(duì)象去。
11.Encapsulate Coolection 封裝集合
有一個(gè)函數(shù)返回一個(gè)集合。讓這個(gè)函數(shù)返回該集合的一個(gè)只讀副本,并在這個(gè)類中提供添加/移除集合元素的函數(shù)。我們常常會(huì)在一個(gè)類中使用集合來(lái)保存一組實(shí)例。這樣的類通常也會(huì)提供針對(duì)該集合的取值/設(shè)值函數(shù)。
???????但是,集合的處理方式應(yīng)該和其他種類的數(shù)據(jù)略有不同。取值函數(shù)不該返回集合自身,因?yàn)檫@會(huì)讓用戶得以修改集合內(nèi)容而集合擁有者卻一無(wú)所悉。也會(huì)對(duì)用戶暴露過(guò)多對(duì)象內(nèi)部數(shù)據(jù)結(jié)構(gòu)信息。如果一個(gè)取值函數(shù)確實(shí)需要返回多個(gè)值,它應(yīng)該避免用戶直接操作對(duì)象內(nèi)所保存的集合。并隱藏對(duì)象內(nèi)與用戶無(wú)關(guān)的數(shù)據(jù)結(jié)構(gòu)。
???????另外,不應(yīng)該為這整個(gè)集合提供設(shè)值函數(shù),但應(yīng)該提供用以為集合添加/移除元素的函數(shù)。這樣,集合擁有者就可以控制集合元素的添加和移除。
???????如果你做到以上幾點(diǎn),集合就被很好地封裝起來(lái),這便可以降低集合擁有者和用戶之間的耦合度。
12.Replace Record with Data Class 以數(shù)據(jù)類取代記錄
? ? ? ?你需要面對(duì)傳統(tǒng)編程環(huán)境中的記錄結(jié)構(gòu)。為該記錄創(chuàng)建一個(gè)“啞”數(shù)據(jù)對(duì)象。 ? ? ? ?記錄型結(jié)構(gòu)是許多編程環(huán)境的共同性質(zhì)。有一些理由使它們被帶進(jìn)面向?qū)ο蟪绦蛑?#xff1a;你可能面對(duì)的是一個(gè)遺留程序,也可能需要通過(guò)一個(gè)傳統(tǒng)API來(lái)與記錄結(jié)構(gòu)交流,或是處理從數(shù)據(jù)庫(kù)讀出的記錄。這些時(shí)候你就有必要?jiǎng)?chuàng)建一個(gè)接口類,用以處理這些外來(lái)數(shù)據(jù)。最簡(jiǎn)單的做法就是先建立一個(gè)看起來(lái)類似外部記錄的類,以便日后將某些字段和函數(shù)搬移到這個(gè)類中。一個(gè)不太常見(jiàn)但非常令人注目的情況是:數(shù)組中的每個(gè)位置上的元素都有特定含義,這種情況下應(yīng)該使用 Replace Array with Object (以對(duì)象取代數(shù)組)。13.?Replace Type Code with Class 以類來(lái)取代類型碼
類之中有一個(gè)數(shù)值類型碼,但它并不影響類的行為。以一個(gè)新的類替換該數(shù)值類型碼。在以C為基礎(chǔ)的編程語(yǔ)言中,類型碼或枚舉值很常見(jiàn)。如果帶著一個(gè)有意義的符號(hào)名,類型碼的可讀性還不錯(cuò)。問(wèn)題在于,符號(hào)名終究只是個(gè)別名,編譯器看見(jiàn)的、進(jìn)行類型檢驗(yàn)的,還是背后那個(gè)數(shù)值。任何接受類型碼作為參數(shù)的函數(shù),所期望的實(shí)際上是一個(gè)數(shù)值,無(wú)法強(qiáng)制使用符號(hào)名。這會(huì)大大降低代碼的可讀性,從而成為bug之源。
???????如果把那樣的數(shù)值換成一個(gè)類,編譯器就可以對(duì)這個(gè)類進(jìn)行類型檢驗(yàn)。只要為這個(gè)類提供工廠函數(shù),你就可以始終保證只有合法的實(shí)例才會(huì)被常見(jiàn)出來(lái),而且他們都會(huì)被傳遞給正確的宿主對(duì)象。
???????但是,在使用Replace Type Code with Class (以類取代類型碼)之前,你應(yīng)該先考慮類型碼的其他替換方式。只有當(dāng)類型碼是純粹數(shù)據(jù)時(shí)(也就是類型碼不會(huì)在switch語(yǔ)句中引起行為變化時(shí)),你才能以類來(lái)取代它。更重要的是:任何switch語(yǔ)句都應(yīng)該運(yùn)用Replace Conditional with Polymorphism (以多態(tài)取代條件表達(dá)式)去掉。為了進(jìn)行那樣的重構(gòu),你首先必須運(yùn)用 Replace Type Code with Subclass (以子類取代類型碼)或Replace Type Code with State/Strategy (以狀態(tài)策略取代類型碼),把類型碼處理掉。
???????即使一個(gè)類型碼不會(huì)因其數(shù)值的不同而引起行為上的差異,宿主類中的某些行為還是可能更適合放置于類型碼類中,因此你還應(yīng)該留意是否有必要使用Move Method(搬移函數(shù))將一兩個(gè)函數(shù)搬過(guò)去。
14.Replace Type Code with Subclasses 以子類來(lái)取代類型碼
??你有一個(gè)不可變的類型碼,它會(huì)影響類的行為。以子類取代這個(gè)類型碼。如果你面對(duì)的類型碼不會(huì)影響宿主類的行為,可以使用Replace Type Code with Class (以類取代類型碼)來(lái)處理它們。但如果類型碼會(huì)影響宿主類的行為,那么最后的辦法就是借助多態(tài)來(lái)處理變化行為。
???????一般來(lái)說(shuō),這種情況的標(biāo)志就是像switch這樣的條件表達(dá)式。這種條件表達(dá)式可能有2種表現(xiàn)形式:switch語(yǔ)句或者if –then-else結(jié)構(gòu)。不論哪種形式,它們都是檢查類型碼值,并根據(jù)不同的值執(zhí)行不同的動(dòng)作。這種情況下,你應(yīng)該以 Replace Conditional with Polymorphism (以多態(tài)取代條件表達(dá)式)進(jìn)行重構(gòu)。但為了那個(gè)順利進(jìn)行那樣的重構(gòu),首先應(yīng)該將類型碼替換為可擁有多態(tài)行為的繼承體系。這樣一個(gè)繼承體系應(yīng)該以類型碼宿主類為基類,并針對(duì)每一種類型碼建立一個(gè)子類。
???????但是以下2種情況你不能那么做1)類型碼值在對(duì)象創(chuàng)建之后發(fā)生了變化;2)由于某種原因,類型碼宿主類已經(jīng)有了子類。如果你恰好面臨這2種情況之一,就需要運(yùn)用 Replace Type Code with StateStrategy (以狀態(tài)策略取代類型碼)。
?????? Replace Type Code with Subclass?(以子類取代類型碼)的主要作用其實(shí)是搭建一個(gè)舞臺(tái),讓Replace Conditional with Polymorphism (以多態(tài)取代條件表達(dá)式)得以一展身手。如果宿主類中并沒(méi)有出現(xiàn)條件表達(dá)式,那么Replace Type Code with Class (以類取代類型碼)更合適,風(fēng)險(xiǎn)也較低。
???????使用Replace Type Code with Subclass (以子類取代類型碼)的另一個(gè)原因是,宿主類中出現(xiàn)了“只與具備特定類型碼之對(duì)象相關(guān)”的特性。完成本項(xiàng)重構(gòu)后,你可以使用 push down Method (函數(shù)下移)和push down field (字段下移)將這些特性推到合適的子類中去,以彰顯它們只與特定情況相關(guān)這一事實(shí)。
?????? Replace Type Code with Subclass?(以子類取代類型碼)的好處在于:它把“對(duì)不同行為的了解”從類用戶那轉(zhuǎn)移到了類自身。如果需要再加入新的行為變化,只需要添加一個(gè)子類就行了。如果沒(méi)有多態(tài)機(jī)制,就必須找到所有條件表達(dá)式,并逐一修改它們。因此,如果為了還有可能加入新行為,這項(xiàng)重構(gòu)將特別有價(jià)值。
15.Replace Type Code with State/Strategy 以狀態(tài)/策略取代類型碼
你有一個(gè)類型碼,它會(huì)影響類的行為,但你無(wú)法提供繼承手法消除它。以狀態(tài)對(duì)象取代類型碼。本項(xiàng)重構(gòu)和Replace Type Code with Subclass (以子類取代類型碼)類似,但如果“類型碼在對(duì)象生命期中發(fā)生變化”或“其他原因使得宿主類不能被繼承”,你也可以使用本重構(gòu)。本重構(gòu)使用State模式和Strategy模式。
?????? State模式和Strategy模式非常相似,因此無(wú)論你選擇其中哪一個(gè),重構(gòu)過(guò)程都是一樣的。“選擇哪個(gè)模式”并非問(wèn)題的關(guān)鍵所在,你只需要選擇更合適特定情境的模式就行了。如果你打算在完成本重構(gòu)后再以Replace Conditional with Polymorphism (以多態(tài)取代條件表達(dá)式)簡(jiǎn)化一個(gè)算法,那么選擇Strategy模式較合適;如果你打算搬移狀態(tài)相關(guān)的數(shù)據(jù),而且你把新建對(duì)象視為一種變遷狀態(tài),就應(yīng)該選擇State模式。
?
16.Replace Subclass with Fieldls 以字段取代子類
你的各個(gè)子類的唯一差別只在“返回常量數(shù)據(jù)”的函數(shù)身上。修改這些函數(shù),使它們返回超類中的某個(gè)(新增)只讀,然后銷毀子類。? ? ? ?建立子類的目的,是為了增加新特性或變化其行為。有一種變化行為被稱為“常量函數(shù)”,它們會(huì)返回一個(gè)硬編碼的值。這東西有其用途:你可以讓不同的子類中的同一個(gè)訪問(wèn)函數(shù)返回不同的值。你可以在超類中將訪問(wèn)函數(shù)聲明為抽象函數(shù),并在不同子類中讓它返回不同的值。
???????盡管常量函數(shù)有其用途,但若與子類中只有常量函數(shù),實(shí)在沒(méi)有足夠的存在價(jià)值。你可以在超類中設(shè)計(jì)一個(gè)與常量函數(shù)返回值相應(yīng)的字段,從而完全除去這樣的子類。如此一來(lái)就可以避免因繼承而帶來(lái)的額外復(fù)雜性。
總結(jié)
以上是生活随笔為你收集整理的重构-改善既有代码的设计:重新组织数据的16种方法(六)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 重构-改善既有代码的设计:重构原则(二)
- 下一篇: 重构-改善既有代码的设计:编写代码22宗