Java, Only Java!

          統計

          留言簿(20)

          積分與排名

          好友空間

          文檔技巧

          閱讀排行榜

          評論排行榜

          《重構》的讀書筆記–方法列表

        1. 第5章 重構列表
        2. 第6章 重新組織函數
        3. 第8章 重新組織數據
        4. 第9章 簡化條件表達式 237
        5. 第10章 簡化函數調用
        6. 第11章 處理概括關系 319

            第5章 重構列表

            5.1 重構的記錄格式103

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

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

            5.2 尋找引用點105

            引用點:就是那些被重構的代碼被哪些代碼調用了。現在的重構工具已經可以簡化這些操作,但是工具永遠都是有限的,除了工具還應該熟悉一些基本的操作手段。

            5.3 這些重構手法有多成熟106

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

            第6章 重新組織函數

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

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

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

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

            6.2 Inline Method(內聯函數)117

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

            6.3 Inline Temp(內聯臨時變量)119

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

            6.4 Replace Temp with Query(以查詢取代臨時變量)120

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

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

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

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

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

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

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

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

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

            6.9 Substitute Algorithm(替換算法)139

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

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

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

            7.1 Move Method(搬移函數)142

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

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

            7.2 Move Field(搬移字段)146

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

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

            7.3 Extract Class(提煉類)149

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

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

            7.4 Inline Class(將類內聯化)154

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

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

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

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

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

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

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

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

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

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

            第8章 重新組織數據

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            8.6 Duplicate Observed Data(復制“被監視數據”)189

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

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

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

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

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

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

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

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

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

            8.10 Encapsulate Field(封裝字段)206

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

            8.11 Encapsulate Collection(封裝集合)208

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

            • Java2:封裝Set
            • Java1.1:封裝Vector
            • 封裝數組

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

            動機:面對舊程序中Record數據結構,新建類取代它
            方法:為該記錄創建一個“啞”數據對象。

            Type Code(類型碼)

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

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

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

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

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

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

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

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

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

            第9章 簡化條件表達式 237

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

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

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

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

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

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

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

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

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

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

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

            • 以break或者continue代替;
            • 也可以通過函數調用和return語句來實現。

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

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

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

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

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

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

            9.8 Introduce Assertion(引入斷言)267

            動機:某段代碼需要對程序狀態顯式地表明某種假設
            方法:以斷言明確表現這種假設
            具體方法: 斷言在 發布的時候統統 被跳過

            第10章 簡化函數調用

            使接口變得更加簡潔易用的重構方法。

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

            10.1 Rename Method(函數改名)273

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

            10.2 Add Parameter(添加參數)275

            動機:被調用的函數需要從調用函數中得到更多的信息
            方法:為被調用的函數添加參數
            抉擇:

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

            10.3 Remove Parameter(移除參數)277

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

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

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

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

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

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

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

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

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

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

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

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

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

            10.9 Introduce Parameter Object(引入參數對象)295

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

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

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

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

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

            10.11 Hide Method(隱藏函數)303

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

            10.12 Replace Constructor with Factory Method(以工廠函數取代構造函數)304

            動機:創建對象時不僅僅是做簡單的構建動作方法:將構造函數替換為工廠模式范例:

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

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

            動機:某個函數返回的對象,需要由函數調用者執行向下轉型(downcast)
            方法:將向下轉型移到函數中

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

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

            • 非受控異常:使用守衛語句檢查這個異常情況;
            • 受控異常:需要修改的調用者函數和被調用者函數,步驟太大,容易出錯??梢韵葎摻ㄒ粋€臨時的中間函數,保留原函數,使所有的調用都改為新函數后,刪除原函數,再修改新函數名稱,即可。

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

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

            第11章 處理概括關系 319

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

            11.1 Pull Up Field(字段上移)320

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

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

            11.2 Pull Up Method(函數上移)322

            動機:有些函數,在各個子類產生相同的結果。
            方法:

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

            11.3 Pull Up Constructor Body(構造函數本體上移)325

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

            • 將共同代碼放在子類構造函數起始處,然后再復制到超類構造函數中。
            • 將子類構造函數中共同代碼刪除,改用調用新建的超類構造函數。

            11.4 Push Down Method(函數下移)328

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

            11.5 Push Down Field(字段下移)329

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

            11.6 Extract Subclass(提煉子類)330

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

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

            11.7 Extract Superclass(提煉超類)336

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

            11.8 Extract Interface(提煉接口)341

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

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

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

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

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

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

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

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

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

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

            主站蜘蛛池模板: 洛扎县| 赤水市| 金沙县| 鸡泽县| 方山县| 九龙县| 永仁县| 化隆| 南乐县| 八宿县| 陕西省| 洛南县| 澄迈县| 巫溪县| 晴隆县| 监利县| 泸溪县| 宁国市| 武宁县| 任丘市| 宕昌县| 西城区| 临江市| 林甸县| 峨眉山市| 福安市| 贺兰县| 白玉县| 武平县| 兴义市| 泽州县| 息烽县| 孟连| 舞阳县| 新津县| 陇川县| 永靖县| 搜索| 马公市| 怀来县| 宜城市|