《重構-改善既有代

重要列表

1、如果你發現自己需要為程序添加一個特性,而代碼結構使你無法很方便地達成目的,那就先重構哪個程序,使特性的添加比較容易的進行,然后再添加特性

2、重構前,先檢查自己是否有一套可靠的測試機制,這些測試必須有自我檢驗能力

3、重構技術就是以微小的步伐修改程序,如果你犯下錯誤,很容易便可發現它

4、任何一個傻瓜都能寫出計算機可以理解的代碼,唯有寫出人類容易理解的代碼,才是優秀的程序員

5、重構(名詞):對軟件內部結構的一種調整,目的是在不改變軟件可觀察行為的前提下,提高其可理解性,降低其修改成本。

6、是不過三,三則重構

7、不要過早發布接口,請修改你的代碼所有權政策,使重構更順暢

8、當你感覺需要撰寫注釋時,請先嘗試重構,試著讓所有注釋都變得多余

9、確保所有的測試都自動化,讓他們檢查自己的測試結果。

10、一套測試就是一個強大的bug偵測器,能夠大大縮減查找bug所需要的時間。

11、頻繁地運行測試,每次編譯請把測試也考慮進去,每天至少執行每個測試一次

12、每當你收到bug報告,請先寫一個單元測試暴露這個bug

13、編寫為臻完善的測試并實際運行,好過對完美測試的無盡等待。

14、考慮可能出錯的邊界條件,把測試火力集中在那里

15、不要因為測試無法捕捉所有bug就不寫測試,因為測試的確定可以捕捉到大多數bug。

一、重構,第一個案例

  • 重構第一步:為即將修改的代碼建立一組可靠的測試環境。
  • 代碼的約小,代碼的功能就越容易管理,代碼的處理和移動也就越輕松。
  • 代碼應該表現自己的目的,這一點非常重要。
  • 函數應該放在它所使用的數據的所屬對象內。
  • 臨時變量往往引發問題,它們會導致大量參數被傳來傳去,而其實完全沒這種必要。
  • 重構的節奏:測試、小修改、測試、小修改、測試、小修改。。。。正是這種節奏讓重構得以快速而安全的前進。

二、重構原則

  • 重構:對軟件內容部結構的一種調整,目的是在不改變軟件可觀察行為的前提下,提高其可理解性,減低其修改成本
  • 使用重構技術開發軟件時,你把自己的時間分配成兩種截然不同的行為:添加新功能,以及重構。添加新功能時,你不應該修改既有代碼,只管添加新功能。通過測試(并讓測試正常運行),你可以衡量自己的工作進度。重構時,你就不能再添加功能,只管改進程序結構。此時,你不應該添加任何測試(除非發現之前有遺漏的東西),只在絕對必要(用以處理接口變化)時才修改測試。
1、重構的目的:
  • 重構改進軟件設計
  • 重構使軟件更容易理解
  • 重構幫助找到bug?
  • 重構提高編譯速度
2、何時重構

幾乎任何情況下我都反對專門撥出時間進行重構。在我看來,重構本身就不是一鍵應該特別拔出時間做的事件,重構應該隨時隨地的進行。你不應該為了重構而重構,你之所以重構,只是因為你想做別的什么事情,而重構可以幫助你把哪些事情做好。

事不過三,三則重構

  • 添加功能時重構
  • 修補錯誤時重構
  • 復審代碼時重構

我們希望程序(1)容易閱讀 (2)所有邏輯都只在唯一地點指定 (3)新的改動不會危及現有行為(4)盡可能簡單表達條件邏輯

重構就是這樣一個過程:它在一個目前可運行的程序上進行,在不改變程序行為的前提下使其具備上述美好性質,使我們能夠繼續保持高速開發,從而增加程序的價值。

3、何時不該重構
  • 編寫新的代碼時
  • 既有代碼根本不能正常運行時,重寫,而非重構
  • 項目已進最后期限,也應該避免重構
4、重構與設計
  • 重構和設計彼此互補。
  • 預先設計不能解決所有問題。要建造一個靈活的解決方案,所需要的成本難以估計。靈活的解決方案比簡單的解決方案復雜很多,通常也更難維護。
  • 重構可以讓軟件設計向簡化前進一大步。重構可以帶來更簡單的設計,同時又不損失靈活性,這也降低了設計過程的難度,減輕了設計壓力。
5、重構與性能
  • 不贊成為了提高設計的純潔性而忽視性能,
  • “編寫快速軟件”的秘密就是:首先寫出課調試的軟件,然后調整它以求獲得足夠的速度。

三、代碼的壞味道

  1. duplicate code 重復代碼
  2. long method? 過長函數(程序越長越難理解),
    如何確認提煉那一段代碼:
    1??尋找注釋,它們通常能指出代碼用途和實際實現手法之間的語義距離
    2??條件表達式和循環常常也是提煉的信號,應該將循環和其內部的代碼提煉到一個獨立的函數中。
  3. large class 過大的類
    1) 如果想利用單個類做太多事情,其內往往就會出現太多實例變量,你可以運用Extract Class 將幾個變量一起提煉至新類。
  4. long Parameter List 過長參數列
    太長的參數難以理解,太多參數會照成前后不一致,不易使用,而且一旦你需要更多數據,就不得不修改它。
  5. Divergent change 發散式變化
    如果某個類經常因為不同的原因在不同的方向上發生變化,divergent change就出現了
  6. shotgun surgery? 散彈式修改
    如果每遇到某種變化,你都必須在許多不同的類中做出許多小修改,就是shotgun sergery
  7. feature Envy 依戀情結
    函數對某個類的興趣高過對自己所處類的興趣
  8. Data Clumps 數據泥團
  9. Primitive Obsession 基本類型偏執
    對象的一個極大的價值在于:他們模糊(甚至打破了恒于基本數據和體積較大類之間的界限)
  10. Switch statements switch 驚悚現身
    面向對象程序的一個最明顯的特征就是:少用switch或者case語句。
    面向對象中的多態概念可以為此帶來優雅的解決辦法。
  11. parallel Inheritance hierarchies 平行繼承體系
    每當你為某個類增加一個子類,必須也為另一個類相應增加一個子類,
  12. Lazy Class 冗贅類
  13. speculative generality? 夸夸其談未來性
    用不上的裝置只會擋住你的路,所以,搬開它
  14. Temporary Field 令人迷惑的暫時字段
  15. Message Chains 過度耦合的消息鏈
  16. middle Man? 中間人
    過度運用委托
  17. Inappropriate Intimacy 過于親密關系
  18. alternative classes with Different Interfaces 異曲同工的類
    兩個函數做同一件事,卻有著不同的簽名
  19. Incomplete Library class? 不完美的庫類
  20. data class? 純稚的數據類
    他們擁有一些字段,以及用于訪問讀寫這些字段的函數,除此之外一無長物
  21. refused bequest 被拒絕的遺贈
    子類應該繼承超類的函數和數據
  22. comments 過多的注釋
    ?

四、構筑測試體系

  • 如果你想進行重構,首要前提就是擁有一個可靠的測試環境。
  • 編寫良好的測試程序,可以極大的提高我的編程速度。
  • 每個類都應該有一個測試函數,并以他來測試這個類。
  • 鐫寫測試代碼的最有用時機是在開始編程之前
  • 編寫未完善的測試,并實際運行,好過對完美測試的無盡等待

五、重構列表

重構的記錄格式

每個重構手法都有如下5個部分

  1. 首先是名稱?
  2. 簡短概要。簡單介紹此重構手法的使用情景,以及它所做的事情。
  3. 動機,介紹“為什么需要這個重構”和“什么情況下不該使用這個重構”
  4. 做法,簡明扼要的一步一步介紹如何進行此重構
  5. 范例 ,以一個十分簡單的例子說明此重構如何運作。
尋找引用點

你應該檢查每一個引用點,確定它的確指向你想要替換的東西

六、重新組織函數

1、extract Method 提煉函數

概要:將一段代碼放進獨立的函數中,并讓函數名稱解釋該函數的用途。

簡短而命名良好的函數。

函數多長才合適?關鍵在于函數名稱和函數本體之間的語義距離,如果提煉可以強化代碼的清晰度,那么就去做,就算函數名稱比提煉出來的代碼還長也無所謂。

以它“做什么”來命名,而不是以它“怎么做”命名

2、Inline method 內聯函數

一個函數的本體與名稱同樣清楚易懂,在函數調用點插入函數本體,然后移除該函數。

如果你手上有一大群組織不合理的函數,你可以將它們都內聯到一個大函數中,再從中提煉組織合理的小函數。

3、Inline Temp 內聯臨時變量

你有一個臨時變量,只被一個簡單表達式賦值過一次,而它妨礙了其他重構手法,將所有對該變量的引用動作,替換為對它賦值的那個表達式。

如果這個臨時變量并未被申明為final,那就將它申明為final,然后編譯,這個可以檢查該臨時變量是否真的只被賦值一次。

4、replace Temp with Query 以查詢代替臨時變量

你的程序以一個臨時變量保存某一表達式的運算結果,將這個表達式提煉到一個獨立的函數中,將這個臨時變量的所有引用點替換為對新函數的調用,此后,新函數就可以被其他函數使用。

臨時變量的問題在于:它們是暫時的,而且只能在所屬的函數內使用。

確保提煉出的函數無任何副作用,也就是說該函數并不修改任何對象內容

代碼組織良好,你往往能夠發現更有效的優化方案,如果沒有進行重構,好的優化方案就可能與你失之交臂。

5、Introduce Explaining Variable? 引入解釋性變量

你有一個復雜的表達式,將該表達式(或者其中一部分)的結果放進一個臨時變量,以此變量名稱來解釋表達式用途。

表達式有可能非常復雜而難以理解。這種情況下,臨時變量可以幫助你講表達式分解為比較容易管理的形式。

6、Split Temporary Variable 分解臨時變量

你的程序有某個臨時變量被賦值超過一次,它既不是循環變量,也不被用于收集計算結果,針對每次賦值,創建一個獨立、對應的臨時變量。

7、Remove Assignment to? Parameters 移除對象參數的賦值

代碼對一個參數進行賦值,以一個臨時變量取代該參數的位置。

不對參數賦值

8、Replace Method with Method Object 以函數對象取代函數

有一個大函數,其中局部變量的使用使你無法差用extract method ,將這個函數放進一個單獨的對象中,如此以來,局部變量就成了對象內的字段。然后你可以在同一個對象中將這個大型函數分解為多個小函數。

9、Substitute algorithm 替換算法

把某個算法替換為另一個更清晰的算法

七、在對象之間搬移特性

1、Move Method 搬移函數

程序中,有個函數與其所駐類之外的另一個類進行更多交流:調用后者,或者被后者調用。在該函數最常引用的類中建立一個有著類似行為的新函數,將舊函數變成一個單純的委托函數,或者是將舊函數完全移除。

檢查源類中被源函數所使用的一切特性(包括字段和函數),考慮它們是否也該被搬遷。

2、Move Field 搬移字段

程序中,某個字段被其所駐類之外的另一類更多的用到,在目標類中新建一個字段,修改源字段的所有用戶,令它們改用新字段。

如果源字段不是private 就必須在源類的所有子類中查找源字段的引用點,并進行相應的替換。

小步前進

3、extract Class 提煉類

某個類做了應該由兩個類做的事。建立一個新類,將相關的字段和函數從舊類搬移到新類。

每次搬遷后,編譯、測試

4、inline Class 將類內聯化

某個類沒有做太多事情,將這個類的所有特性搬移到另一個類中,然后移除原類。

如果一個類不再承擔足夠責任、不再有單獨存在的理由(這通常是因為此前的重構動作移走了這個類的責任)。

5、Hide Delegate 隱藏“委托關系”

客戶通過一個委托類來調用另一個對象,在服務類上建立客戶所需的所有函數,用以隱藏委托關系。

封裝意味每個對象都應該盡可能減少了解系統的其他部分,

6、Remove Middle Man 移除中間人

某個類做了過多的簡單委托動作,讓客戶直接調用委托類

重構的意義就在于:你永遠不必說對不起----只要把出問題的地方修補好就行了。

7、Introduce foreign Method 引入外加函數

需要為提供服務的類增加一個函數,但你無法修改這個類。在客戶類中建立一個函數,并以第一參數形式傳入一個服務類示例。

不要忘記,外加函數總歸是權宜之計。如果有可能,你依然應該將這些函數搬移到它們的理想家園。

8、Introduce Local Extension 引入本地擴展

你需要為服務類提供一些額外函數,但你無法修改這個類,建立一個新類,使它包含這些額外函數。讓這個擴展品成為源類的子類或者包裝類。

八、重新組織數據

1、Self Encapsulate Field? 自封裝字段

你直接訪問一個字段,但與字段間的耦合關系逐漸變得笨拙,為這個字段建立取值/設值函數,并且只以這些函數來訪問字段。

2、Replace Data Value with Object 以對象取代數據值

你有一個數據項,需要與其他數據和行為一起使用才有意義,將數據項變成對象

為待替換數值新建一個類,在其中申明一個final字段,其類型和源類中的待替換數值類型一樣。然后再新類中加入這個字段的取值函數,再加上一個接受此字段為參數的構造函數。

值對象應該是不可修改的內容。如果你希望給這個對象加入一下可以修改的數據,你需要將這個對象變成一個引用對象。

3、change Value To Reference? 將值對象改為引用對象

你從一個類衍生出許多彼此相等的實例,希望將它們替換為同一個對象。將這個值對象變成引用對象。

4、changeReference to Value 將引用對象改為值對象

你有一個引用對象,很小且不可變,而且不易管理,將它變成一個值對象

如果該對象目前還不是不可變的,就使用remove setting Method 直到它變成不可變為止

如果無法將該對象修改為不可變的,就放棄使用本項重構。

5、Replace array withobject 以對象取代數組

你有一個數組,其中的元素各自代表不同的東西,以對象替換數組,對于數組中的每一個元素,以一個字段表示。

6 、Duplicate observed data 復制“被檢視數據”

你有一些領域數據置身于GUI控件中,而領域函數需要訪問這些數據,將該數據復制到一個領域對象中,建立一個observe模式。已同步領域對象和GUI對象內的重復數據。

分層良好的系統,應該將處理用戶界面和處理業務邏輯的代碼分開。

7、change Unidirectional association to Bidirectional 將單向關聯改為雙向關聯

兩個類都需要使用對方特性,但其間只有一條單向鏈接,添加一個反向指針,并使修改函數能夠同時更新兩條鏈接。

8、change Bidirectional association to Unidirectional 將雙向關聯改為單向關聯

兩個類之間有雙向關聯,但其中一個如今不再需要另一個類的特性,去除不必要的關聯。

9 replace magic number withSymbolic constant 以字面常量取代魔法數

你有一個字面數值,帶有特別含義,創建一個常量,根據其意義為他命名,并將上述的字面數值替換為這個常量。

10、encapsulate Field 封裝字段

類中存在一個public 字段 ,將它聲明為private ,并提供相應的訪問函數。

面向對象的首要原則之一就是封裝,或者稱為“數據隱藏”

11 encapsulate collection 封裝集合

有個函數返回一個集合。讓這個函數返回該集合的一個只讀副本,并在這個類中提供添加移除集合元素的函數。

取值函數不該返回集合自身,因為這會讓用戶得以修改集合內容而集合擁有者一無所知,這也會對用戶暴露過多對象內部數據結構的信息。不應該為這整個集合提供一個設置函數,但應該提供用以為集合添加/移除元素的函數。

12、replace record with data class? 以數據取代記錄

你需要面對傳統編程環境中的記錄結構,為該記錄創建一個“啞”數據對象。

13、replace type code with class 以類取代類型碼

類之中有個數值類型碼,但它并不影響類的行為,以一個新的類替換該數值類型碼

14 replace type code with subClass 以子類取代類型碼

你有一個不可變的類型碼,他會影響類的行為,以子類取代這個類型碼。

為了能夠順利進行重構,首先應該將類型碼替換為可擁有多態行為的繼承體系,這樣的一個繼承體系應該以類型碼的宿主類為基類,并針對每一種類型碼各建立一個子類。

15、replace type code with State/strategy 以state/strategy 取代類型碼

你有一個類型碼,他會影響類的行為,但你無法通過繼承手法消除它,以狀態對象取代類型碼

16、replace subclass with fields 以字段取代子類

個股子類唯一的差別只在“返回常量”的函數身上,修改這些函數,是他們返回超類中的某一個(新增)字段,然后銷毀子類。

建立子類的目的是為了增加新特性或者變化其行為。

盡管常量函數有其用途,但若子類中只有常量函數,實在沒有足夠的存在價值

九、簡化條件表達式

較之過程化程序而言,面向對象程序的條件表達式通常比較少,這是因為很多條件行為都被多態機制處理掉了,多態之所以更好,是因為調用者無需了解條件行為的細節,因此條件的擴展更為容易。

1、decompose coditional 分解條件表達式

你有一個復雜的條件(if-then-else)語句。從if、then、else 三個段落中分別提煉出獨立函數。

對于條件邏輯,將每個分支條件分解成新函數還可以 突出條件邏輯,更清楚的表明每個分支的作用,并且突出每個分支的原因。

2、consolidate conditional expression 合并條件表達式

一系列條件測試,都得到相同的結果,將這些測試合并為一個條件表達式,并將這個條件表達式提煉成一個獨立函數。

將檢查條件提煉成一個獨立函數,對于厘清代碼意義非常有用,因為他把描述“做什么”的語句換成了“為什么這樣做”

如果你認為這些檢查的確彼此獨立,的確不應該被視為同一次檢查,那么就不用使用本項重構。因為在這種情況下,你的代碼已經清除表達出自己的意義。

3、consolidate duplicate conditional fragments 合并復雜的條件片段

在條件表達式的每一個分支上有著相同的一段代碼。將這段復雜代碼搬移到條件表達式之外。

4 remove control flag? 移除控制標記

在一系列布爾表達式中,某個變量帶有“控制標記(control flag)”的作用,用break語句或者return語句取代控制標記

5、replace nested conditional with guard clause 以衛語句取代嵌套表達式

函數中的條件邏輯使人難以看清正常的執行路徑,使用衛語句表現所有特殊情況。

如果兩條分支都是正常行為,就應該使用形如if..else..的條件表達式;如果某個條件極其罕見,就應該單獨檢查該條件,并在改條件為真時立刻從函數中返回。這樣的單獨檢查常常被稱為“衛語句”。

給某一條分支以特別的重視。

衛語句要不就從函數中返回,要不就拋出一個異常。

6、replace conditional with polymorphism 以多態取代條件表達式

有一個條件表達式,他根據對象類型的不同而選擇不同的行為。將這個條件表達式的每一個分支放進一個子類內的覆寫函數中,然后將原始函數聲明為抽象函數。

7、Introduce Null object 引入null對象

你需要再三檢查某個對象是否為null,將null值替換為null對象

8 Introduce assertion 引入斷言

某段代碼需要對程序狀態做出某種假設,以斷言明確表現這種假設

注意,不要濫用斷言。請不要使用它來檢查“你認為應該為真”的條件,請只使用它來檢查“一定必須為真”的條件。

十、簡化函數調用

明確的將“修改對象狀態”的函數(修改函數)和“查詢對象狀態”的函數(查詢函數)分開設計。

良好的接口只向用戶展示必選展示的東西。如果一個接口暴露過多的細節,你可以將不必要暴露的東西隱藏起來,從而改進接口的質量。

1 rename Method 函數改名

函數的名稱未能揭示函數的用途,修改函數名稱。

給函數命名有個好方法:首先考慮應該給這個函數寫上一句怎樣的注釋,然后想辦法將注釋變成函數名。

想成為一個真正的編程高手,起名的水平是至關重要的。

2、add parameter? 添加參數

某個函數需要從調用端得到更多的信息。為此函數添加一個對象參數,讓改對象參數帶進所需要的信息。

除了添加參數之外,你常常還有其他選擇,只要可能,其他選擇都比添加參數要好,因為它們不會增加參數列的長度。

3、remove parameter 移除參數

函數主體不再需要某個參數,將改參數去除。

參數代表著函數所需要的信息,不同的參數值有不同的意義、。

4、separate Query from modifier? 將查詢函數和修改函數分離

某個函數即返回對象狀態值,又修改對象狀態,建立兩個不同的函數,其中一個負責查詢,另一個負責修改。

任何有返回值得函數,都不應該有看得見的副作用。

如果調用者將返回值賦值給了一個臨時變量,你應該去掉這個臨時變量

5、parameterize method? 令函數攜帶參數

若干函數做了類似的工作,但在函數本體中卻包含了不同的值。建立單一函數,以參數表達那些不同的值。

以“可將少量數值視為參數”為依據,找出帶有重復性的代碼

6、replace parameter with explicit methods 以明確函數取代參數

你有一個函數,其中完全取決于參數值而采用不同的行為。針對該參數的每一種可能值,建立一個獨立函數

如果某個參數有多種可能的值,而函數內又以條件表達式檢查這些參數值,并根據不同參數值做出不同的行為,那么久應該使用本項重構、但是如果參數值不會對函數行為有太多影響,你就不應該使用本項重構

7、preserve whole object 保持對象完整

你從某個對象中取出若干值,將它們作為某一次函數調用時的參數,改為傳遞整個對象。

如果這會使你的依賴結構惡化,那么久不該使用本項重構

8、replace parameter with methods 以函數取代參數

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

如果有必要將參數的計算過程提煉到一個獨立函數中。

9 introduce parameter object? 引入參數對象

有些參數總是很自然的同事出現,以一個對象取代這些參數、

參數對象中的值可以把其中所有的字段都設置為final,只能由構造函數來賦值。這樣可以避免很多困擾。

10 remove setting method 移除設置函數

類中的某個字段應該在對象創建時被設值,然后就不再改變。去掉該字段的所有設值函數

如果你為某個字段提供了設值函數,這就暗示這個字段可以被改變。

11 hide method 隱藏函數

有一個函數,從來沒有被其他任何類用到,將這個函數改為private

12、replace constructor with factory method

你希望在創建對象時不僅僅是做簡單的建構動作。將構造函數替換為工廠函數。

13、encapsulate downcast? 封裝向下轉型

某個函數返回的對象,需要有函數調用者執行向下轉型,將向下轉型動作移動到函數中

14、replace error code with exception 以異常取代錯誤碼

某個函數返回一個特定的代碼,用以表示某個錯誤情況,改用異常

15 replace exception with test? 以測試代替異常

面對一個調用者可預先檢查的條件,你拋出了一個異常。修改調用者,是它在調用函數之前先做檢查。

十一、處理概況關系

1、pull up field

兩個子類擁有相同的字段,將該字段移至超類

本項重構從兩方面減少重復:首先它去除了重復的數據申明,其次它使你可以將使用該字段的行為從子類移至超類,從而去除重復的行為。

2、pull up method 函數上移

有些函數,在各個子類中產生完全相同的結果,將該函數移至超類

只要系統內出現重復,你就會面臨“修改其中一個缺未能修改另一個”的風險

如果這些函數看上去做了相同的事情,但是并不完全一致,可以使用 substitute 愛老公人 讓它們變得完全一致。

3、pull up constructor body? 構造函數本體上移

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

4、push down method 函數下移

超類中的某個函數只與一部分(而非全部)子類有關。將這個函數移到相關的那些子類中去。?

5、push down field 字段下移

超類中某個字段只被部分(而非全部)子類用到,將這個字段移到需要它的那些子類中去。

6、extract subclass 提煉子類

類中的某些特性只能被某些(而非全部)實例用到,新建一個子類,將上面所說的那一部分特性移到子類中。

7、extract superClass 提煉超類

兩個類有相似特性,為這兩個類建立一個超類,將相同特性移至超類

8、extract interface 提煉接口

若干客戶使用類接口中的同一子集,或者兩個類的接口有部分相同。將相同的子集提煉到一個獨立接口中。

9、collapse hierarchy 折疊繼承體系

超類和子類之間無太大區別。將它們和為一體。

10、form template method 塑造模板函數

有一些子類,其中相應的某些函數以相同順序執行類似的操作,但各個操作的細節上有所不同。將這些操作分別放進獨立函數中,并保持它們都有相同的簽名,于是原函數也就變得相同了,然后將原函數上移至超類。

11、replace inheritance with delegation? 以委托取代繼承

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

12 replace delegation with inheritance 以繼承取代委托

你在兩個類之間使用委托關系,并經常為整個接口編寫許多簡單至極的委托函數,讓委托類繼承受托類。

十二、大型重構

1、tease apart inheritance? 梳理并分解繼承體系

某個繼承體系同時承擔兩項責任。建立兩個繼承體系,并通過委托關系讓其中一個可以調用另一個。

2、convert procedural design to objects 將過程化設計轉化為對象設計

你手上有一些傳統過程化風格的代碼。將數據記錄變成對象,將大塊的行為分為小塊,并將行為移入相關對象中。

3、separate domain from presentation 將領域和表述/顯示分離

某些GUI類之中包含了領域邏輯。將領域邏輯分離出來,為它們建立獨立的領域類。

4、extract hierarchy 提煉繼承體系

你有某個類做了太多工作,其中一部分工作是以大量條件表達式完成的。建立繼承體系,以一個子類表示一種特殊情況。

將一個極度混亂的設計方案梳理出來,可能需要數周甚至數月的時間。你可以先進行本重構中的一些簡易步驟,稍微休息一下,再花幾天時間編寫一些能體現產出的代碼。

十三、重構,復用與實現

如何把重構和軟件業者(特別是那些開發大型項目的軟件業者)的日常事務結合起來

通過重新組織軟件結構,重構使設計思路更詳盡明確。重構被用于開發框架、抽取可復用組件、使軟件框架更清晰、使新功能的增加更容易。重構可以幫助你充分利用以前的投資,減少重復勞動,使程序更簡潔有力。

應對并處理變化,使軟件開發的根本復雜性之一

重構也可以帶來短期利益,讓軟件更容易修改、更易維護。重構只是一種手段,不是目的。它是“”程序員或者程序開發團隊如何開發并維護自己軟件”這一更寬廣場景的一部分。

對員工進行培訓、盡量獲取短期利益、減少開銷、安全引入新技術

十四、重構工具

重構的最大障礙之一就是:幾乎沒有工具對它提供支持。

refactoring browser 工具

十五、小結

這些技術如此精彩,可它們卻僅僅只是個開始:因為你還不知道何時應該使用它們、合適不應該使用;何時開始、何時停止;何時前進、何時等待。使重構能夠成功的,不是前面各種獨立的技術,而是這種節奏

1、隨時挑一個目標:某個地方的代碼開始發臭,你就應該將問題解決掉。你應該朝目標前進,達成目標后即停止。

2、沒把握就停下來:朝目標前進的過程中,可能會有這樣的時候:你無法證明你所做的一切能夠保成程序原本的語義。此時你就應該停下來,如果代碼已經改善了一些,就發布你的成果;如果沒有,就撤銷所有的修改。

3:學習原路返回。重構的原則不好學,而且很容易遺矢準頭。如果重構已經失控,你應該回到最近一個沒有出錯的狀態,然后逐一重復剛才做過的重構,每次重構之后一定要運行所有測試。

4、二重奏:和別人一起重構、可以收到更好的效果

重構時你的目標之一就是保持代碼的功能完全不變,即不多也不少。對于那些需要修改的東西,列個清單把它們記錄下來,需要添加或者修改的測試用例,需要進行的其他重構、需要撰寫的文檔、需要畫的圖……都暫時記在卡上,這樣就不會忘掉這些需要完成的工作。千萬別讓這些工作打亂你手上的工作。重構完成之后,再去做這些事情也不遲。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/443904.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/443904.shtml
英文地址,請注明出處:http://en.pswp.cn/news/443904.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Myeclipse 6.5 優化

1、取消自動validation validation有一堆,什么xml、jsp、jsf、js等等,我們沒有必要全部都去自動校驗一下,只是需要的時候才會手工校驗一下! 取消方法: windows–>perferences–>MyEclipse Enterprise Workbench–…

Linux簡單命令集——head

NAME head - output the first part of files 輸出文件的第一部分SYNOPSIS head [OPTION]... [FILE]...head [選項]... [文件]...head命令在屏幕上顯示指定文件file的開頭若干行,行數由參數值來確定。顯示行數的默認值是10。-c, --bytes[-]NUM print the firs…

操作系統中避免死鎖的銀行家算法【表面C++實際C語言】一學就廢的菜雞代碼

文章目錄銀行家算法實驗原理數據結構初始化輸出資源分配量安全性算法銀行家算法完整代碼測試數據測試結果第一題第二題銀行家算法 銀行家算法是一種最有代表性的避免死鎖的算法。在避免死鎖方法中允許進程動態地申請資源,但系統在進行資源分配之前,應先…

GitHub 使用指南

目錄切換分支刪除已有文件只刪除遠程倉庫中的文件,不刪除本地倉庫中的文件同時刪除遠程倉庫和本地倉庫中的文件提交文件git查看本地分支連接的是哪個遠程分支切換分支 查看本地和遠程所有分支 git branch -a當前本地分支為綠色,當前所在分支前帶有“*”號…

百度EBG測試部AI測試工程師面經

百度EBG測試部AI測試工程師 1、自我介紹 自我介紹盡量多說一點,她會問你“還有嗎?” 2、項目介紹 簡歷上的項目都讓介紹了。會問這個項目是做什么的,是由誰開發,項目如何得到的,在測試過程中實現了什么。 3、自己…

一學就廢的并查集它來了

文章目錄題目描述輸入輸出樣例輸入樣例輸出提示算法思想代碼實現尋找根節點匯總連接情況完整代碼關于flag的初值題目描述 某省調查城鎮交通狀況,得到現有城鎮道路統計表,表中列出了每條道路直接連通的城鎮。省政府“暢通工程”的目標是使全省任何兩個城…

一道很簡單的貪心算法題~【貪心:我不要臉的伐?】

文章目錄題目描述輸入輸出樣例輸入樣例輸出C語言代碼實現思路排序處理完整代碼C代碼實現排序完整代碼彩蛋題目描述 小健有一家自己的商店,主營牛奶飲品,最近資金緊張,他想以盡可能低的價格進購足夠的牛奶以供日常的需要。所以小健想要求助你…

Eclipse 中修改java編譯版本

修改方法是: 1:Preferences-->Java-->Compiler->Compiler compliance level,選擇一個需要的版本,比如從默認的1.4改為5.0 2:如果只想修改一個工程的Compiler compliance level,就右單擊工程,選擇屬…

百戰c++(1)

Public和private的區別 public和private是類里的關鍵字,用于規定類內數據或者成員函數的訪問權限。private類型的數據或者函數,只能在相應的類內被訪問,而public類型的數據或者函數被訪問的權限比較寬,還可以在其它類或者其它函數…

一學就廢的三種簡單排序【冒泡、插入、選擇】

文章目錄其他排序算法冒泡排序算法實現代碼實例插入排序算法實現代碼實例選擇排序算法實現代碼實例其他排序算法 一學就廢的歸并排序 冒泡排序 排列順序從前到后或者從后往前都可,本文選擇從后到前的順序,升序排列:比較相鄰兩個元素&#x…

百戰c++(2)

delete 和 delete []的真正區別 delete 對應 new delete[]對應new[]對于簡單類型包括簡單類型數組,delete 與delete[]沒有區別。對于自定義類型數組,delete 只會刪除一個元素,delete 則會刪除所有元素。 指針和數組的區別 野指針是什么 野指…

shell預先定義的特殊變量

文章目錄$#$*$$$# 表示命令行上參數的個數,但不包括shell腳本名本身 為腳本ex1賦予兩個變量,測試$#的輸出結果 [cmybogon test2]$ . ex1 ma.c mb.c 2 # echo $# 7 # cat $1 $2 $3 | wc -l 2 # echo $#腳本ex1的具體內容 [rootlocalhost test]$ cat ex1…

Linux實驗一:常用的Linux命令

文章目錄一、實驗目的二、實驗要求三、實驗內容1、系統的使用2、命令的使用3、文件操作4、系統詢問與權限口令5、其它常用命令四、實驗操作1、基本命令的使用2、文件和目錄操作3、創建用戶帳戶一、實驗目的 1、熟悉Linux的桌面環境; 2、了解Linux所安裝的軟件包 3、…

Linux實驗二:vi編輯器的使用

文章目錄一、實驗目的二、實驗要求三、實驗內容1、創建文件2、編輯文件一、實驗目的 1、練習并掌握Linux提供的vi編輯器來編譯C程序 2、學會利用gcc、gdb編譯、調試C程序 3、本次實驗的目的是讓同學們了解如何使用vi編輯器進行創建和編輯文件 二、實驗要求 1、文件編輯器vi…

百戰c++(os1)

Linux中的鎖 互斥鎖:mutex,用于保證在任何時刻,都只能有一個線程訪問該對象。當獲取鎖操作失敗時,線程會進入睡眠,等待鎖釋放時被喚醒 讀寫鎖:rwlock,分為讀鎖和寫鎖。處于讀操作時&#xff0…

Linux實驗三:Shell編程

文章目錄一、實驗目的二、實驗要求三、實驗內容1、通配符的使用2、重定向3、管道4、shell變量5、建立下面的腳本,運行并分析輸出結果,并給出代碼注釋。6、編寫腳本一、實驗目的 1.為文件擴展名使用通配符 2.標準輸入、標準輸出和標準錯誤的重定向 3.使…

a href=#與 a href=javascript:void(0) 的區別

a href"#"> 點擊鏈接后&#xff0c;頁面會向上滾到頁首&#xff0c;# 默認錨點為 #TOP<a href"javascript:void(0)" onClick"window.open()"> 點擊鏈接后&#xff0c;頁面不動&#xff0c;只打開鏈接<a href"#" οnclick&…

Linux實驗四:編譯和調試工具的使用

文章目錄一、實驗目的&#xff1a;二、實驗要求三、實驗內容四、實驗操作1、用gcc編譯程序&#xff0c;寫出編譯過程&#xff0c;并給出運行結果。2、調試程序&#xff0c;要求用gdb進行調試并給出修改方案。3、make的使用一、實驗目的&#xff1a; 1、練習并掌握Linux提供的v…

Linux實驗五:Linux環境下的C語言編程

文章目錄一、實驗目的&#xff1a;二、實驗要求三、實驗內容1、編寫一段C語言程序使其完成&#xff1a;父進程創建兩個子進程&#xff0c;每個進程都在屏幕上顯示自己的進程ID號。2、上機調試下面的程序&#xff0c;觀察運行結果&#xff0c;分析原因。3、利用兩個管道進行雙向…

百戰c++(4)

1.求下面函數的返回值&#xff08;微軟&#xff09; int func(x) { int countx 0; while(x) { countx ; x x&(x-1); } return countx; } 假定x 9999。 答案&#xff1a;8 思路&#xff1a;將x轉化為2進制&#xff0c;看含有的1的個數。 2. 什么是“引用”&…