Java, Only Java!

          統(tǒng)計

          留言簿(20)

          積分與排名

          好友空間

          文檔技巧

          閱讀排行榜

          評論排行榜

          《重構(gòu)》的讀書筆記–方法列表

        1. 第5章 重構(gòu)列表
        2. 第6章 重新組織函數(shù)
        3. 第8章 重新組織數(shù)據(jù)
        4. 第9章 簡化條件表達式 237
        5. 第10章 簡化函數(shù)調(diào)用
        6. 第11章 處理概括關系 319

            第5章 重構(gòu)列表

            5.1 重構(gòu)的記錄格式103

            這個格式可以作為自己未來記錄重構(gòu)手法的標準,方便查閱自己常用的和總結(jié)的重構(gòu)方式:

            • 名稱:重構(gòu)詞匯表
            • 概要:適用的情景,以及所做的事情
            • 動機:說明“為什么需要”和“什么情況不做”
            • 做法:重構(gòu)的步驟(看似簡單,其實很重要,因為重構(gòu)不建議跳躍完成,最好保證每次一小步都是正確的)
            • 范例:通過例子正確的理解重構(gòu)手法的操作方式

            5.2 尋找引用點105

            引用點:就是那些被重構(gòu)的代碼被哪些代碼調(diào)用了。現(xiàn)在的重構(gòu)工具已經(jīng)可以簡化這些操作,但是工具永遠都是有限的,除了工具還應該熟悉一些基本的操作手段。

            5.3 這些重構(gòu)手法有多成熟106

            學習和使用這些重構(gòu)僅僅是個起點,業(yè)務在變、工具在變、語言在變、對事物的理解也在變,因此完全挺有自己的重構(gòu)手段才是最終的目標。

            第6章 重新組織函數(shù)

            • Extract Method是本章的重點,用于解決Long Methods問題;
            • Extract Method的困難是處理局部變量,特別是局部變量中的臨時變量,為此需要本章中的其他方法作為補充。

            6.1 (P110)Extract Method(提煉函數(shù))

            動機:Long Methods問題或者代碼需要Comments問題;
            方法:(可以使用IDE提供的重構(gòu)工具,下面的具體操作的原理)

            • 創(chuàng)造一個新函數(shù),根據(jù)這個函數(shù)的意圖來對它命名;
            • 將提煉的代碼拷貝到新建函數(shù)中;
            • 檢查提煉的代碼是否引用了“作用域限于原函數(shù)”的變量(局部變量、原函數(shù)參數(shù))
            • 檢查提煉的代碼是否包含了“僅用于被提煉代碼段”的臨時變量
              • 如果有,在目標函數(shù)中將之聲明為局部變量
            • 檢查被提煉代碼段,看看是否有任何局部變量的值被他改變,
              • 如果臨時變量的值被修改了,嘗試將提煉的代碼變?yōu)橐粋€查詢,將結(jié)果返回給相關變量;
              • 如果不好操作,或者被修改的變量多于一個,可以嘗試(分解臨時變量/以查詢替換變量)等手段了。
            • 將被提煉代碼段中需要被讀取的局部變量,當參數(shù)傳給目標函數(shù)
            • 處理完所有局部變量后,編譯,檢查
            • 在源函數(shù)中,將被提煉的代碼替換為對目標函數(shù)的調(diào)用
            • 編譯,檢查,測試

            6.2 Inline Method(內(nèi)聯(lián)函數(shù))117

            動機:當函數(shù)內(nèi)部的代碼與其名稱一樣易懂,可以在函數(shù)調(diào)用點插入函數(shù)本體,移除該函數(shù)。
            補充:對有一群不太合理的函數(shù),可以先內(nèi)聯(lián)為一個長函數(shù),然后再提煉出合理的小函數(shù)

            6.3 Inline Temp(內(nèi)聯(lián)臨時變量)119

            動機:當臨時變量只被一個簡單的表達式賦值一次,而且它妨礙其他重構(gòu)方法時,才需要重構(gòu)
            條件:Inline Temp多半是為Replace Temp with Query(以查詢?nèi)〈R時變量)準備
            方法:將所有對該變量的引用動作替代成對它賦值的表達式本身。

            6.4 Replace Temp with Query(以查詢?nèi)〈R時變量)120

            動機:你的程序以一個臨時變量保存一個表達式的計算結(jié)果
            方法:將表達式提煉出獨立的函數(shù),然后臨時變量的調(diào)用替換成新函數(shù)的調(diào)用。此后新函數(shù)也能被調(diào)用。
            具體方法: 將提煉出來的函數(shù)用private修飾,如果獨立函數(shù)有副作用,那對它進行Separate Query from Modifier(將查詢函數(shù)和修改函數(shù)分離)

            6.5 Introduce Explaining Variable(引入解釋性變量)124

            將復雜表達式(或者其中一部分)的結(jié)果賦值給一個臨時變量,用臨時變量名稱來解釋表達式的用途

            6.6 Split Temporary Variable(分解臨時變量)128

            臨時變量被賦值超過一次,但是它既不是循環(huán)變量也不是被用于收集計算結(jié)果
            原因:一個變量應該承擔一個責任,如果被賦值多次很可能承擔了多個責任
            方法:針對每次賦值,創(chuàng)建新的臨時變量

            6.7 Remove Assignments to Parameters(移除對參數(shù)的賦值)131

            java是值傳遞,對參數(shù)的任何修改都不會對調(diào)用端產(chǎn)生影響,所以對于用過引用傳遞的人可能會發(fā)生理解錯誤
            參數(shù)應該僅表示“被傳遞過來的東西”

            6.8 Replace Method with Method Object(以函數(shù)對象取代函數(shù))135

            動機:在大型函數(shù)內(nèi),對局部變量的使用導致難以使用Extract Method(提煉函數(shù))進行重構(gòu)
            方法:將這個函數(shù)放入一個對象里,局部變量變成對象成員變量,然后可以在同一對象中將這個大型函數(shù)分解為多個小型函數(shù)。
            原因:局部變量會增加分解函數(shù)的困難度

            6.9 Substitute Algorithm(替換算法)139

            把某個算法替換成更清晰的方法(算法)。

            第7章 在對象之間搬移特性141

            本章的重點是搬移(Move)。“決定把責任放在哪里”是面向?qū)ο笤O計中最重要的事情之一,但是剛開始設計時,由于技術和業(yè)務知識的不足無法保證做出的決定是正確的,那么后期調(diào)整中將“責任”搬移到正確的對象中就是重要的重構(gòu)手段。而Move Method(142)和Move Field(146)就是搬移“責任”過程中最基本的兩個方法。

            7.1 Move Method(搬移函數(shù))142

            動機:類中某個函數(shù)與其他類交互過多
            方法:將該函數(shù)搬移到交互最多的類里面,將舊函數(shù)變成委托函數(shù)或者刪除。
            具體方法:

            • 檢查源類中被源函數(shù)使用的一切特性,如果特性被其他函數(shù)使用,考慮這些函數(shù)一起搬移
            • 檢查源類的子類和超類,看看是否有該函數(shù)的聲明,如果出現(xiàn),很可能不能搬移。
            • 目標類需要使用源類的特性:
              1. 將該特性轉(zhuǎn)移到目標類;
              2. 建立目標類到源類之間引用。
              3. 將源類作為參數(shù)傳給目標類
              4. 將該特性作為參數(shù)傳給目標類
            • 如果源函數(shù)包含異常處理,需要考慮是在目標類還是源函數(shù)處理

            7.2 Move Field(搬移字段)146

            動機:類中某個字段被其他類頻繁使用(包括:傳參數(shù)、調(diào)用取值函數(shù)、調(diào)用設值函數(shù))
            方法:將該字段搬移到目標類
            具體方法:

            • 先封裝這個字段;
            • 在目標類建立這個字段,并且封裝;
            • 設定目標對象;
            • 替換對源字段的引用為目標類的取值函數(shù)

            7.3 Extract Class(提煉類)149

            動機:一個類做了兩個類的事
            方法:

            • 建立新類,將相應的字段和函數(shù)放到新類
            • 使用Move Field重構(gòu);
            • 使用Move Method重構(gòu);
            • 判斷是否需要公開新類。

            7.4 Inline Class(將類內(nèi)聯(lián)化)154

            動機:某個類功能太少,與Extract Class(提煉類)相反 方法:將這個類的所有特性搬移到另一類中,移除該類。
            原因:多次Extract Class后,原類大部分功能被移走,將這個萎縮類與其他相近的類合并

            7.5 Hide Delegate(隱藏“委托關系”)157

            動機:客戶端通過委托類來取得另一個對象的信息
            方法:在服務類上建立客戶端所需數(shù)據(jù)的函數(shù),然后隱藏委托關系
            依據(jù):符合“封裝”的特性。當委托類發(fā)生變化不會對客戶端造成影響,減少客戶端與調(diào)用者之間的耦合性。

            7.6 Remove Middle Man(移除中間人)160

            動機:某個類做了過多的委托動作
            方法:讓客戶端直接調(diào)用委托類,與Hide Delegate(隱藏“委托關系”)相反 依據(jù):當原委托類的特性越來越多,服務類的委托函數(shù)將越來越長,需要讓客戶端直接調(diào)用,避免服務類淪為中間人。

            7.7 Introduce Foreign Method(引入外加函數(shù))162

            動機:使用的類無法提供某個功能,但是又不能修改該類
            方法:新建函數(shù),并將服務類的對象實例作為參數(shù)傳入。
            具體動機:如果需要為服務類增加大量的方法,請考慮使用Introduce Local Extension(引入本地擴展)

            7.8 Introduce Local Extension(引入本地擴展)164

            動機:使用的類無法提供多個功能,但是又不能修改該類
            方法:建立新的類,在新類中建立需要的功能函數(shù),可以作為服務類的子類實現(xiàn)新的類,也可以包裝服務類實現(xiàn)新的類。
            具體情況:

            • 首選子類,工作量最小
              • 但是必須在對象創(chuàng)建期實施,如果不行就只能選擇包裝類;
              • 子類的對象不能修改父類的數(shù)據(jù),否則建議選擇包裝類,因為會導致父類對象與子類對象的數(shù)據(jù)可能不一致
            • 包裝類需要實現(xiàn)被包裝對象的所有接口,工作量很大。

            第8章 重新組織數(shù)據(jù)

            本章重點是如何更好地封裝各種類型的數(shù)據(jù)。最常用的手段就是Self Encapsulate Field(171)

            8.1 Self Encapsulate Field(自封裝字段)171

            動機:直接訪問一個字段,但是字段之間的耦合關系逐漸變得笨拙。
            方法:自封裝就是在對于類內(nèi)部的字段也封裝一個設值取值的函數(shù)。
            爭論:字段訪問方式是直接訪問還是間接訪問一致爭論不斷
            間接訪問的好處:

            • 子類可以通過覆蓋一個函數(shù)來改變獲取數(shù)據(jù)的途徑;
            • 支持更靈活的數(shù)據(jù)管理,如延遲加載(需要用到才加載)等。直接訪問的好處:代碼容易讀懂,理解不需要轉(zhuǎn)換為取值函數(shù)。

            8.2 Replace Data Value with Object(以對象取代數(shù)據(jù)值)175

            動機:假如一個數(shù)據(jù)項需要與其他數(shù)據(jù)一起使用才有意義。數(shù)據(jù)已經(jīng)不僅僅由一條簡單的數(shù)據(jù)項組成,例如:電話號碼
            方法:將數(shù)據(jù)變成對象。

            8.3 Change Value to Reference(將值對象改為引用對象)179

            動機:一個類有許多相等的實例,希望把這些相等的實例統(tǒng)一為一個對象,方便統(tǒng)一修改或者進行相等性比較
            方法:將值對象變成引用對象
            “引用對象”與“值對象”的區(qū)別:

            • 每個引用對象代表著現(xiàn)實中一個對象,使用對象的一致性用來檢測兩個對象是否相等,即(==)
            • 值對象完全由其自身的值來相互區(qū)分,需要重寫一些方法用來檢測兩個對象是否相等。(重寫equals()和hashcode()方法)具體方法:
            • 需要使用工廠模式來創(chuàng)建對象
            • 需要另一個對象(或者是自身)作為訪問點來訪問定義的引用對象,對象用Dictionary或者HashTable來保存對象
            • 決定對象是預先創(chuàng)建還是動態(tài)創(chuàng)建

            8.4 Change Reference to Value(將引用對象改為值對象)183

            動機:引用對象,很小且不可變,而且不易管理

            • 很小:創(chuàng)建許多也不會消耗太多內(nèi)存
            • 不可變:不需要復雜的管理代碼,也不需要考慮同步問題,還會造成別名問題具體方法:
            • 檢查重構(gòu)目標是否是不可變對象或者可修改成不可變對象
              • 使用Remove Setting Method變成不可變對象
              • 如果無法修改成不可變對象,就放棄重構(gòu)
            • 重寫hashCode和equals()方法
            • 取消使用的工廠模式,并將對象的構(gòu)造函數(shù)設為public

            8.5 Replace Array with Object(以對象取代數(shù)組)186

            動機:如果數(shù)據(jù)存儲的值代表不同的東西。
            方法:將數(shù)組變成對象,數(shù)組的每個元素用字段表示

            8.6 Duplicate Observed Data(復制“被監(jiān)視數(shù)據(jù)”)189

            動機: 有業(yè)務數(shù)據(jù)置身于GUI控件中,而與業(yè)務相關的函數(shù)需要訪問這些數(shù)據(jù)
            方法:將業(yè)務數(shù)據(jù)復制到業(yè)務類中。建立Observer模式,同步UI和業(yè)務類的數(shù)據(jù)。

            8.7 Change Unidirectional Association to Bidirectional(將單向關聯(lián)改為雙向關聯(lián))197

            動機:兩個類相互之間都需要對方的數(shù)據(jù),但是相互之間只有一條單向的連接
            這個重構(gòu)需要添加測試,因為“反向指針”很容易造成混亂。
            具體方法:

            • 在被引用類添加字段,保存引用類的指針;
            • 判斷由哪個類來控制關聯(lián)關系;
              • 如果兩者都是引用對象,且關聯(lián)關系為“一對多”的關系,那么就由“擁有單一引用”的對象作為控制者;
              • 如果A對象是B對象的部件,則由B對象負責控制關系;
              • 如果兩者都是引用對象,且關聯(lián)關系為“多對多”的關系,那么隨意確定一個對象作為控制者。
            • 在被控端建立輔助函數(shù),命名清晰地描述其用途;
              • 如果修改函數(shù)在控制端,則由其負責更新反向指針;
              • 如果修改函數(shù)在被控制端,則在控制端建立一個修改反射指針的函數(shù),由修改函數(shù)調(diào)用其修改反向指針。
              • 兩者是一對多關系,有單一引用承擔控制關聯(lián)關系責任

            8.8 Change Bidirectional Association to Unidirectional(將雙向關聯(lián)改為單向關聯(lián))200

            動機:兩個類有雙向關聯(lián),但是一個類不再需要另一個類的特性
            原因:

            • 雙向關聯(lián)可能造成僵尸對象,不能被清除釋放內(nèi)存。
            • 使兩個類存在耦合關系,一個類的變化會導致另一類的變化。方法:去除雙向關聯(lián)
              困難:檢查可行性

            8.9 Replace Magic Number with Symbolic Constant(以字面常量取代魔法數(shù))204

            動機:有一個字面常量(除了0和1之外)
            方法:創(chuàng)建常量賦值以該字面常量,給予命名。

            8.10 Encapsulate Field(封裝字段)206

            動機:一個類有public字段
            將它聲明為private,并提供相應的訪問函數(shù)

            8.11 Encapsulate Collection(封裝集合)208

            動機:類中使用集合,但是集合不能提供給用戶直接操作,而是提供函數(shù)操作集合,降低用戶與集合之間的耦合度
            方法:提供函數(shù)返回集合的只讀副本,并提供增加和刪除集合元素的函數(shù)
            具體方法:

            • Java2:封裝Set
            • Java1.1:封裝Vector
            • 封裝數(shù)組

            8.12 Replace Record with Data Class(以數(shù)據(jù)類取代記錄)217

            動機:面對舊程序中Record數(shù)據(jù)結(jié)構(gòu),新建類取代它
            方法:為該記錄創(chuàng)建一個“啞”數(shù)據(jù)對象。

            Type Code(類型碼)

            常見于過去的C語言編程中,因為沒有枚舉,所以采用類型碼的方式標注。這個重構(gòu)遇到的機會比較小

            8.13 Replace Type Code with Class(以類取代類型碼)218

            動機:類中的數(shù)值類型碼不影響類的行為
            方法:以一個新類替代類型碼

            8.14 Replace Type Code with Subclasses(以子類取代類型碼)223

            動機:有一個不可變的類型碼且影響類的行為
            標志:switch或者if-then-else類的條件表達式,這個重構(gòu)是Replace Conditional with Polymorphism的準備工具
            方法:以子類取代這個類型碼

            8.15 Replace Type Code with State/Strategy(以State/Strategy取代類型碼)227

            動機:有一個類型碼且影響類的行為,但是無法通過繼承消除(類型碼可變化)
            方法:以狀態(tài)對象取代。

            8.16 Replace Subclass with Fields(以字段取代子類)232

            動機:各個子類唯一區(qū)別只在“返回常量的數(shù)據(jù)”的函數(shù)上
            方法:修改這些函數(shù)使它們返回超類的某個(新增)字段,然后銷毀子類。

            第9章 簡化條件表達式 237

            條件邏輯非常復雜,也非常難以理解,通過重構(gòu)將復雜的邏輯展現(xiàn)為簡單的邏輯塊。
            有些重構(gòu)方法看起來非常簡單,因為重構(gòu)最重要的思想不是方法有多精妙,而是傳達了一個小步快走的理念。就是一次只完成一個小重構(gòu),然后測試確保沒有錯誤。然后,再進行下一個小重構(gòu)和測試。從而整個大重構(gòu)通過多個簡單的小重構(gòu)完成,避免大重構(gòu)出錯后需要全部回滾的問題。

            9.1 Decompose Conditional(分解條件表達式)238

            動機:if-then-else語句,不同分支做不同事動機成大型函數(shù),本身就難以閱讀,尤其在帶有復雜條件的邏輯中。方法:

            • 將if語句提煉為函數(shù)
            • 將then和else段落提煉為函數(shù)
            • 對于存在嵌套的條件邏輯,先判斷是否可以用Replace Nested Conditional with Guard Clauses(以衛(wèi)語句取代嵌套條件表達式)消除。不行再分解每個條件

            9.2 Consolidate Conditional Expression(合并條件表達式)240

            動機:有一系列條件判斷都服務于共同的目標
            方法:將這些條件判斷合并為同一個表達式,再將這個表達式提煉為獨立函數(shù)
            原因:

            • 只是一次條件檢查,只是存在多個并列條件需要檢查而已
            • 為Extract Method(提煉函數(shù))做準備,通過函數(shù)名告知“為什么這么做”

            9.3 Consolidate Duplicate Conditional Fragments(合并重復的條件片段)243

            動機:在條件表達式的不同分支中存在相同的代碼
            方法:將這些重復代碼搬移到條件表達式之外,多行代碼還可以再提煉為獨立函數(shù)。
            例如:當try和catch執(zhí)行相同代碼,可以將代碼移到final區(qū)段。

            9.4 Remove Control Flag(移除控制標記)245

            動機:在循環(huán)執(zhí)行的程序段中,某個變量定義為判斷條件中的控制標記(control flag),增加了代碼理解的復雜度
            方法:

            • 以break或者continue代替;
            • 也可以通過函數(shù)調(diào)用和return語句來實現(xiàn)。

            9.5 Replace Nested Conditional with Guard Clauses(以衛(wèi)語句取代嵌套條件表達式)250

            衛(wèi)語句:如果某個條件極其罕見,就應該單獨檢查該條件,并在該條件為真時立刻從函數(shù)中返回,這樣的單獨檢查被稱為“衛(wèi)語句”(guard clauses)
            動機:函數(shù)中的條件邏輯使人難以看清正確的執(zhí)行路徑。
            方法:使用衛(wèi)語句表現(xiàn)所有的特殊情況

            9.6 Replace Conditional with Polymorphism(以多態(tài)取代條件表達式)255

            動機:存在條件表達式根據(jù)對象的類型不同選擇不同的行為
            方法:將表達式分支放進不同子類,然后重寫方法,將原始函數(shù)提煉為抽象函數(shù)。

            9.7 Introduce Null Object(引入Null對象)260

            動機:需要再三檢查對象是否為null
            方法:將null值替代為null對象,如果原始類不允許修改可以使用Null接口來檢查“對象是否為Null”。

            9.8 Introduce Assertion(引入斷言)267

            動機:某段代碼需要對程序狀態(tài)顯式地表明某種假設
            方法:以斷言明確表現(xiàn)這種假設
            具體方法: 斷言在 發(fā)布的時候統(tǒng)統(tǒng) 被跳過

            第10章 簡化函數(shù)調(diào)用

            使接口變得更加簡潔易用的重構(gòu)方法。

            • 修改函數(shù)名稱,使之容易理解;
            • 縮短參數(shù)列表;
            • 不同的功能分離到不同的函數(shù)中;
            • 隱藏函數(shù),提升接口的質(zhì)量。

            10.1 Rename Method(函數(shù)改名)273

            動機:函數(shù)的名稱不能說明函數(shù)的用途
            方法:將舊函數(shù)代碼搬移到新函數(shù),舊函數(shù)跳轉(zhuǎn)到新函數(shù)。

            10.2 Add Parameter(添加參數(shù))275

            動機:被調(diào)用的函數(shù)需要從調(diào)用函數(shù)中得到更多的信息
            方法:為被調(diào)用的函數(shù)添加參數(shù)
            抉擇:

            • 現(xiàn)有參數(shù)是否提供足夠的信息?
            • 這個函數(shù)是否應該移動到擁有該信息的對象中?
            • 加入新參數(shù)是否合適?
            • 如果需要的參數(shù)過多,是否需要使用Introduce Parameter Object(引入?yún)?shù)對象)?

            10.3 Remove Parameter(移除參數(shù))277

            動機:函數(shù)不需要某個參數(shù)(不需要了就放棄,保留也需要付出代價)
            方法:

            • 如果是獨立的函數(shù),直接將該參數(shù)移除
            • 如果是多態(tài)函數(shù),不能移除,就增加一個新的沒有這個參數(shù)的函數(shù),使調(diào)用者的工作得到簡化

            10.4 Separate Query from Modifier(將查詢函數(shù)和修改函數(shù)分離)279

            動機:某個函數(shù)既修改對象狀態(tài),又返回對象狀態(tài)值。(使調(diào)用者擔心誤操作修改了不應該修改的數(shù)據(jù),增加調(diào)用者的操作負擔)
            本質(zhì):函數(shù)功能簡潔、明確,如果一個函數(shù)具備多個功能,就把它們分離成多個函數(shù)。
            方法:建立兩個不同的函數(shù),其中一個負責查詢,另一個負責修改。
            原則:

            • 任何一個有返回值的函數(shù)都不應該有看得到的副作用。
            • 編碼中主要考慮的不是代碼的效率,而是代碼的易讀性,效率可以在未來上線的時候再根據(jù)實際需要調(diào)整。多線程:將修改和查詢函數(shù)封裝在一個同步函數(shù)中分開調(diào)用。

            10.5 Parameterize Method(令函數(shù)攜帶參數(shù))283

            動機:幾個函數(shù),做了類似的工作,只是代碼中的系數(shù)不同
            方法:建立單一函數(shù),以參數(shù)作為系數(shù)

            10.6 Replace Parameter with Explicit Methods(以明確函數(shù)取代參數(shù))285

            動機:函數(shù)依賴于參數(shù)值的不同而采取不同的行為
            方法:針對該參數(shù)的每個可能值,建立獨立函數(shù)。
            對比:與Parameterize Method(令函數(shù)攜帶參數(shù))相反,但是目的都是把復雜的邏輯判斷消除 目的:提供清晰的入口。
            如果參數(shù)值對函數(shù)行為影響不大,不應該采用此方法。

            10.7 Preserve Whole Object(保持對象完整)288

            動機:從某個對象取若干個值,把他們作為參數(shù)傳給函數(shù)
            方法:改為調(diào)用整個對象
            目的:避免過長參數(shù)列表
            缺陷:如果傳遞的是值,那么函數(shù)只依賴那些值;如果傳遞的是對象,函數(shù)則依賴對象,會導致耦合
            注意:有時候函數(shù)使用了很多來自某個對象的數(shù)據(jù),那么應該考慮使用(Move Method)將這個函數(shù)移到關系密切的對象中

            10.8 Replace Parameter with Methods(以函數(shù)取代參數(shù))292

            動機:對象調(diào)用某個函數(shù),并將所得結(jié)果作為參數(shù)傳遞給另一個函數(shù),而接受該參數(shù)的函數(shù)本身也能夠調(diào)用前一個函數(shù)
            方法:讓參數(shù)接受者去除該項參數(shù),并直接調(diào)用前一個函數(shù)

            10.9 Introduce Parameter Object(引入?yún)?shù)對象)295

            動機:有些參數(shù)總是自然地同時出現(xiàn)
            方法:用一個對象把這些參數(shù)包裝起來進行傳遞
            目的:

            • 縮短參數(shù)列表長度;
            • 函數(shù)具有一致性,降低理解和修改代碼的難度

            10.10 Remove Setting Method(移除設值函數(shù))300

            動機:類的某個字段應該對象創(chuàng)建的時候被設置,然后不再改變
            方法:去掉該字段的設置函數(shù)

            • 如果對參數(shù)的運算很簡單,而且只有一個構(gòu)造函數(shù),就可以直接在構(gòu)造函數(shù)中初始化。
            • 如果修改復雜,或者有多個函數(shù)試圖改變這個字段,那么就需要提供一個獨立函數(shù),并給予獨立函數(shù)一個清楚表達用途的名字
            • 如果是子類希望修改超類的字段
              • 那么最好是使用超類的構(gòu)造器實現(xiàn)改變;
              • 或者通過擁有能夠清楚表達用途的名字的函數(shù)來實現(xiàn)。
            • 如果修改集合字段,請使用Encapsulate Collection(208)實現(xiàn)。

            10.11 Hide Method(隱藏函數(shù))303

            動機:有一個函數(shù),從來沒有被任何類調(diào)用
            方法:將該函數(shù)設為private
            補充:函數(shù)可見度不夠,在編譯的時候就可以發(fā)現(xiàn);而函數(shù)過見度過高,則需要通過一些工具(Lint)來輔助檢查。

            10.12 Replace Constructor with Factory Method(以工廠函數(shù)取代構(gòu)造函數(shù))304

            動機:創(chuàng)建對象時不僅僅是做簡單的構(gòu)建動作方法:將構(gòu)造函數(shù)替換為工廠模式范例:

            • 根據(jù)整數(shù)(實際是類型碼)創(chuàng)建對象;
            • 根據(jù)字符串創(chuàng)建子類對象;
            • 以函數(shù)創(chuàng)建子類;

            10.13 Encapsulate Downcast(封裝向下轉(zhuǎn)型)308

            動機:某個函數(shù)返回的對象,需要由函數(shù)調(diào)用者執(zhí)行向下轉(zhuǎn)型(downcast)
            方法:將向下轉(zhuǎn)型移到函數(shù)中

            10.14 Replace Error Code with Exception(以異常取代錯誤碼)310

            動機:某個函數(shù)返回一個特定的代碼,表示某個錯誤的情況
            方法:取消那個代碼判斷,改用拋出異常
            范例:

            • 非受控異常:使用守衛(wèi)語句檢查這個異常情況;
            • 受控異常:需要修改的調(diào)用者函數(shù)和被調(diào)用者函數(shù),步驟太大,容易出錯。可以先創(chuàng)建一個臨時的中間函數(shù),保留原函數(shù),使所有的調(diào)用都改為新函數(shù)后,刪除原函數(shù),再修改新函數(shù)名稱,即可。

            10.15 Replace Exception with Test(以測試取代異常)315

            動機:本該由調(diào)用者自行檢查的條件,由被調(diào)用者拋出了一個可控異常。
            方法:修改調(diào)用者,使它在調(diào)用函數(shù)之前做檢查。
            補充:異常就應該放在可能發(fā)生異常的地方使用。即可以預測的,可以通過檢查避免的,那就是錯誤,不該發(fā)生;不能預測的,無法通過檢查避免的,那就是異常。例如:賬戶余額小于取錢數(shù)目,申請取錢這個就是錯誤;賬戶余額大于取錢數(shù)目,取不出錢來就是異常。

            第11章 處理概括關系 319

            概括關系(generalization,即繼承關系、泛化關系)

            11.1 Pull Up Field(字段上移)320

            動機:兩個子類擁有相同的字段
            方法:

            • 將該字段移動到超類,去除重復數(shù)據(jù)聲明;
            • 將使用該字段的行為搬移到超類,去除關于這個字段的重復行為。
            • 考慮對超類的該字段使用Self Encapsulate Field(171)

            11.2 Pull Up Method(函數(shù)上移)322

            動機:有些函數(shù),在各個子類產(chǎn)生相同的結(jié)果。
            方法:

            • 將該函數(shù)移動到超類
            • 如果被提升的函數(shù)引用了子類中的函數(shù)
              • 如果可以將引用函數(shù)提升,就一起提升
              • 如果不可以將引用函數(shù)提升,可以在超類里面那個抽象函數(shù)

            11.3 Pull Up Constructor Body(構(gòu)造函數(shù)本體上移)325

            動機:你在各個子類擁有一些構(gòu)造函數(shù),它們的本地幾乎完全一致
            方法:在超類新建一個構(gòu)造函數(shù),并在子類構(gòu)造函數(shù)中調(diào)用它。
            具體方法:

            • 將共同代碼放在子類構(gòu)造函數(shù)起始處,然后再復制到超類構(gòu)造函數(shù)中。
            • 將子類構(gòu)造函數(shù)中共同代碼刪除,改用調(diào)用新建的超類構(gòu)造函數(shù)。

            11.4 Push Down Method(函數(shù)下移)328

            動機:超類中的某個函數(shù)只與部分而非全部子類有關
            方法:將這個函數(shù)移到相關的子類去。

            11.5 Push Down Field(字段下移)329

            動機:超類中的某個字段只被部分而非全部子類使用
            方法:將這個字段移到需要它的那些子類去。

            11.6 Extract Subclass(提煉子類)330

            動機:類中的某些特性只被部分實例用到。
            方法:新建一個子類,將上面所說的那一部分特性移到子類中。
            具體情況:

            • 并不是出現(xiàn)類型碼就表示需要用到子類,可以在委托和繼承之間做選擇。
            • 為子類新建構(gòu)造函數(shù),
              • 子類構(gòu)造函數(shù)與超類構(gòu)造函數(shù)擁有相同的參數(shù)列表,并且直接調(diào)用超類構(gòu)造函數(shù)
              • 如果需要隱藏子類,可使用Replace Constructor with Factory Method(以工廠函數(shù)取代構(gòu)造函數(shù))
            • 找出超類調(diào)用點
              • 如果超類構(gòu)造函數(shù)與子類不同,通過rename method方法可以解決。
              • 如果不需要超類實例,可以將超類聲明為抽象類。
            • 逐一使用函數(shù)下移和字段下移將源類的特性移動到子類。

            11.7 Extract Superclass(提煉超類)336

            動機:兩個類有相似特性。
            方法:為兩個類建立一個超類,將相同特性移至超類。
            補充:Extract Class,Extract Subclass,Extract Superclass對比學習。

            11.8 Extract Interface(提煉接口)341

            動機:多個用戶只使用類接口中的同一子集,或者兩個類的接口有部分相同。
            方法:將相同子集提煉到獨立的接口中。
            區(qū)別:提煉超類是提煉共同代碼,提煉接口時提煉共同接口。
            具體動機:如果某個類在不同環(huán)境下扮演截然不同的角色,使用接口就是個好主意。接口還能幫助類隱藏一些對外的函數(shù)接口。

            11.9 Collapse Hierarchy(折疊繼承體系)344

            動機:超類和子類之間區(qū)別不大。
            方法:將它們合為一體。

            11.10 Form TemPlate Method(塑造模板函數(shù))344

            動機:你有一些子類,其中相應的函數(shù)以相同順序執(zhí)行類似的操作,但各個操作的細節(jié)有所不同。
            方法:將這些小操作分別放進獨立函數(shù)中,并保持它們都有相同的簽名,于是原函數(shù)也變得相同了。然后將原函數(shù)上移至超類,運用多態(tài)來避免重復代碼。這樣的原函數(shù)就是Template Method。
            原因:雖然使用了繼承,但是函數(shù)重復應盡量避免。

            11.11 Replace inherited with Delegation(以委托取代繼承)352

            動機:某個子類只使用超類接口中一部分,或是根本不需要繼承而來的數(shù)據(jù)方法:在子類中新建一個字段用以保存超類,調(diào)整子類函數(shù),令它委托超類,然后去掉兩者之間的繼承關系。

            11.12 Replace Delegation with Inherited(以繼承取代委托)352

            動機:在兩個類之間使用委托關系,并經(jīng)常為整個接口編寫許多極簡單的委托函數(shù),方法:讓委托類繼承受托類。注意:

            • 如果并沒有使用受托類的所有函數(shù),那么就不要使用這個方法。因為子類應該總是遵循超類的接口,如果委托過多可以通過Remove Middle Man(160)方法讓客戶端調(diào)用受托函數(shù),或者Extract Superclass(336)讓兩個類的接口提煉到超類中;還可以使用Extract Interface(341)方法。
            • 如果受托對象被不止一個其他對象共享,而且受托對象是可變的時候,那么這種情況下,不能將委托關系替換為繼承關系,因為這樣就無法共享數(shù)據(jù)了。數(shù)據(jù)共享是委托關系的一種重要功能。
          • posted on 2019-01-16 17:50 zYx.Tom 閱讀(249) 評論(0)  編輯  收藏 所屬分類: 7.學習日志

            主站蜘蛛池模板: 巍山| 名山县| 武威市| 兴宁市| 中方县| 清水河县| 瑞金市| 祁连县| 石狮市| 汝南县| 凤庆县| 格尔木市| 五原县| 乌海市| 勃利县| 德惠市| 九台市| 镇赉县| 苏尼特左旗| 沈丘县| 东至县| 曲沃县| 台东市| 通渭县| 东乡| 沁阳市| 辽宁省| 潼关县| 浙江省| 黑龙江省| 垣曲县| 邯郸市| 卢氏县| 贞丰县| 浙江省| 兴文县| 营山县| 启东市| 嘉鱼县| 十堰市| 谷城县|