重構與模式:改善代碼三部曲中的第三部

重構與模式:改善代碼三部曲中的第三部 

    

 

1、改善代碼的三部曲

 

   《設計模式》-> 《重構》-> 《重構與模式》。也就是設計->重構->重構出新設計。程序員

   《設計模式》主要詳細說明20幾種模式,爲咱們帶來了常見設計問題的經典解決方案,從而改變了整個面向對象開發的面貌。爲設計而著。算法

   《重構》改善既有代碼的設計,總結了咱們會用到的各類重構手法,爲咱們帶來了一種改進代碼的高效過程,從而完全改變了面向對象設計的方式。側重去除壞代碼的味道。編程

    《重構與模式》是設計模式相關的重構。模式不是設計出來的,是重構出來的。好的設計也不是設計出來的,是重構出來的。不要怕改變,只要改變得法,變就再也不是災難,而是進步的良機。側重設計模式+重構手段。設計模式

      在閱讀重構與模式以前,最好熟讀前面兩本:《設計模式》和《重構》。安全

      設計模式表明了傳統的軟件開發思想:好的設計會產生好的軟件,所以在實際開發以前,值得花時間去作一個全面而細緻的設計。重構表明了敏捷軟件開發的浪潮:軟件並非在一開始就能夠設計得天衣無縫的,所以能夠先進行實際開發,而後經過對代碼不斷的進行小幅度的修改來改善其設計。兩者從不一樣角度闡述了設計的重要性。
      有些人在編寫任何代碼以前,都要很早地爲模式作計劃,而有些人在編寫了大量代碼以後纔開始添加模式。
第二種使用模式的方式就是重構,由於是要在不增長系統特性或者不改變其外部行爲的狀況下改變系統的設計。
有些人在程序中加入模式,只是由於以爲模式可以使程序更容易修改;更多人這樣作只是爲了簡化目前的設計。
若是代碼已經編寫,這兩種情形都是重構,由於前者是經過重構使修改更容易,然後者則是經過重構在修改後進行整理。
雖然模式是在程序中可以看到的東西,可是模式也是一種程序轉換。
     重構是實現設計模式的一種手段,設計模式每每也是重構的目的。
 

2、重構與模式的原因


     應該經過重構實現模式、趨向模式和去除模式(refactoring to, towards, and away from pattern),而不是在預先設計中使用模式,也再也不過早的在代碼中加入模式。這技能避免過分設計,又不至於設計不足。
 1. 過分設計:代碼的靈活性和複雜性超出所需。有些開始設計的時候,認爲某些地方會頻繁的改動,甚至開始使用了某種設計模式預留擴展,可是後來卻沒怎麼動,也就是致使了廢設計和功能.
 2. 設計不足
    產生設計不足的緣由:
    1)程序員沒有時間,沒有抽出時間,或者時間不容許進行重構
    2)程序員在何爲好的軟件設計方面知識不足
    3)程序員被要求在既有系統中快速的添加新功能
    4)程序員被迫同時進行太多項目
    長期的設計不足,會使軟件開發節奏變成「快,慢,更慢」,可能的後果是:
    1.0版本很快就交付了,可是代碼質量不好
    2.0版本也交付了,但質量低劣的代碼使咱們慢下來
    在企圖交付將來版本時,隨着劣質代碼的倍增,開發速度也愈來愈慢,最後人們對系統、程序員乃至使你們陷入這種境地的整個過程都失去了信心
    到了4.0版本時或者以後,咱們意識到這樣確定不行,開始考慮推倒重來
 3. 測試驅動開發和持續重構,
  測試驅動開發和持續重構提供了一種精益、迭代和訓練有素的編程風格,可以最大程度的有張有弛,提升生產率。「迅速而又從容不迫」
  使用測試驅動開發和持續重構的益處:
   1)保持較低的缺陷數量
   2)大膽的進行重構
   3)獲得更加簡單、更加優秀的代碼
   4)編程時沒有壓力
  模式和重構之間存在着自然聯繫,模式是你想達到的目的地,而重構則是從其餘地方抵達這個目的地的條條道路。
 4.演進式設計
演進式設計即趨向性設計,主要是避免過分設計。
經過重構產生設計結構,也就是經過重構實現模式或者重構趨向模式。爲設計而設計的思路並不適合大項目,按部就班從重構到設計模式纔是設計模式的王道。

敏捷開發中常常採用的演進式架構設計:架構

不少程序員可能都碰見過這種事:某塊代碼亟待修改,卻沒有人願意接手。爲何會這樣?這段代碼正巧是兩個組件間的接口,修改工做太過困難。而在演進式設計中,咱們經常會作這種修改。代碼應當是"活的"而且是"可生長"的,決不能無視強烈的變化需求 而保持一成不變。正由於如此,演進式設計能夠提升設計質量,進而提升整個系統的質量。框架

 

第6章建立

6.1 用Creating Method替換構造函數
  當類中有多個構造函數,所以很難決定在開發期間用哪個時,能夠用可以說明意圖的返回對象實例的Creation Method替換構造函數
動機:
  Creation Method——類中的一個靜態或者非靜態的負責實例化類的新實例方法。因Creating Method命名沒有限制,因此能夠取最能表達所建立對象的名字。
  類中有太多構造函數→提煉類或者提煉子類 或者 用Creation Method替換構造函數來澄清構造函數的意圖
優缺點:
  + 比構造函數可以更好的表達所建立的實例種類
  + 避免了構造函數的侷限,好比兩個構造函數的參數數目和類型不能相同
  + 更容易發現無用的建立代碼
  - 建立方式是非標準的,有的用new實例化,而有的用Creation Method實例化
變體:
  不須要爲每一個對象的配置都設立一個Creation Method,非必要狀況下能夠添加參數來減小Creation Method的數量
  當Creation Method過多的分散了類的主要職責是,應該考慮將相關的Creation Method重構爲一個Factory
6.2 將建立知識搬移到Factory
  當用來實例化一個類的數據和代碼在多個類中處處都是時,能夠講有關建立的知識搬移到一個Factory中
動機:
  建立蔓延——將建立的職責放在了不該該承擔對象建立任務的類中,是解決方案蔓延中的一種,通常是以前的設計問題致使。
  使用一個Factory類封裝建立邏輯和客戶代碼的實例化選項,客戶能夠告訴Factory實例如何實例化一個對象,而後用同一個Factory實例在運行時執行實例化。
  Factory不須要用具體類專門實現,可使用一個接口定義Factory,而後讓現有的類實現這個接口。
  若是Factory中建立邏輯過於複雜,應將其重構爲Abstract Factory,客戶代碼能夠配置系統使用某個ConcreteFactory(AbstractFactory的一個具體實現)或者默認的ConcreteFactory。
  只有確實改進了代碼設計,或者沒法直接進行實例化時纔有足夠的理由進行Factory重構
優缺點:
  + 合併建立邏輯和實例化選項
  + 將客戶代碼與建立邏輯解耦
  - 若是能夠直接實例化,會使設計複雜化
6.3 用Factory封裝類
  當直接實例化處在同一包結構中、實現統一接口的多個類。能夠把類的構造函數聲明爲非公共的,並經過Factory來建立它們的實例
動機:
  能夠經過Factory將一組客戶並不需關心的子類屏蔽到包內部。
  若是類共享一個通用的公共接口、共享相同的超類、而且處在同一包結構中,該重構可能有用。
優缺點:
  + 經過意圖導向的Creation Method簡化了不一樣種類實例的建立
  + 經過隱藏不須要公開的類減小了包的「概念重量」
  + 幫助嚴格執行「面向接口編程,而不是面向實現」這一格言
  - 當須要建立新種類的實例時,必須更新Creation Method
  - 當客戶只能得到Factory的二進制代碼而沒法得到源碼時,對Factory的定製將受到限制
6.4 用Factory Method引入多態建立
  當一個層次中的類都類似的實現一個方法,只是對象建立的步驟不一樣時,能夠建立調用Factory Method來處理實例化方法的惟一超類版本
動機:
  Factory Method是OOP中最多見的模式,因其提供了多臺建立對象的方法
  使用Factory Method後的代碼每每比在類中賦值方法來建立自定義對象要簡單
  使用Factory Method的主要狀況:
    當兄弟子類實現了除對象建立步驟外都很類似的方法時
    當超類和子類實現了除對象建立步驟外都很類似的方法時
優缺點:
  + 減小因建立自定義對象而產生的重複代碼
  + 有效的表達了對象建立發生的位置,以及如何重寫對象的建立
  + 強制Factory Method使用的類必須實現統一的類型
  - 可能會向Factory Method的一些實現者傳遞沒必要要的參數
6.5 用Builder封裝Composite
  當構造Composite是重複的、複雜的且容易出錯的工做時,經過使用Builder處理構造細節來簡化構造過程。
動機:
  構造Composite是重複的、複雜的、容易出錯的工做,經過使用Builder處理構造細節來簡化構造過程
  Builder模式很擅長處理繁重的、複雜的構造步驟。
  Builder模式的意圖:將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。
優缺點:
  + 簡化了構造Composite的客戶代碼
  + 減小了建立Composite的重複和易出錯的本性
  + 在客戶代碼和Composite之間實現了鬆耦合
  + 容許對已封裝的Composite或複雜對象建立不一樣的表示
  - 接口可能不會很清楚的表達其意圖
6.6 內聯Singleton
  當代碼須要訪問一個對象,可是不須要對象的全局入口時,能夠把Singleton的功能搬移到一個保存並提供對象訪問入口的類中。刪除Singleton。
動機:
  Singleton意圖:確保一個類僅有一個實例,並提供一個訪問它的全局訪問點
  保持暴露對象和保護對象之間的平衡對維護系統的靈活性是相當重要的
  任何全局數據在被證實是無害以前都是有害的
  若是遇到本不應實現爲Singleton的Singleton,不要猶豫,內聯它!
優缺點:
  + 使對象的協做變得更明顯和明確
  + 保護了單一的實例,且不須要特殊的代碼
  - 當在許多層次間傳遞對象實例比較困難的時候,會使設計變得複雜

第7章 簡化

  咱們所編寫的絕大部分代碼都不會從一開始就很簡單。
  算法常常會由於支持多種變化而變得複雜。
  控制狀態轉換的轉換的邏輯每每會變得愈來愈複雜。
7.1 組合方法
  當你沒法迅速的理解一個方法的邏輯時,把方法的邏輯轉換成幾個同一層面上的、可以說明意圖的步驟。
動機:
  Composed Method由對其餘方法的調用組成,好的Composed Method的代碼都在細節的同一層面上。
  Composed Method通常不會引入性能問題
優缺點:
  + 清晰的描述了一個方法所實現的功能以及如何實現
  + 把方法分解成命名良好的、處在細節的同一層面上的行爲模塊,以此來簡化方法
  - 可能會產生過多的小方法
  - 可能會使調試變得困難,由於程序的邏輯分散在許多小方法中
Composed Method指導原則:
  Composed Method都很小。通常在5行左右,不多超過10行
  刪除重複代碼和死代碼。除去明顯的和微妙的代碼重複,除去沒有被使用的代碼,以減小方法的代碼量
  表達意圖。清楚的命名程序中的變量、方法和參數,使它們明確表達意圖。
  簡化。轉換代碼,使它儘量簡單。
  使用細節的統一層面。當把一個方法分解成一組行爲時,要保證這些行爲在細節的類似層面上。
7.2 用Strategy替換條件邏輯
  當方法中條件邏輯控制着應該執行計算的哪一個變體時,爲每一個變體建立一個Strategy並使方法把計算委託到Strategy實例。
 
動機:
  ——爲算法的各個變體生成一系列的類,並用Strategy的一個實例裝配主類,主類在運行時委託到該Strategy實例
  複雜的條件邏輯是最常致使複雜度上升的地點之一
 
優缺點:
  + 經過減小或去除條件邏輯使算法變得清晰易懂
  + 經過把算法的變體搬移到類層次中簡化了類
  + 容許在運行時用一種算法替換另外一種算法
  - 當應用基於繼承的解決方案或「 簡化條件表達式」中的重構更簡單時,會增長設計的複雜度
  - 增長了算法如何獲取或接收上下文類數據的複雜度
7.3 將裝飾功能搬移到Decorator
  當代碼向類和核心職責提供裝飾功能時,能夠考慮將裝飾代碼搬移到Decorator
  不管多麼喜歡一個模式,不要在沒必要要的時候使用它
 
優缺點:
  + 把裝飾功能從類中移除,從而簡化類
  + 有效的把類的核心職責和裝飾功能區分開來
  + 能夠去除幾個相關類中重複的裝飾邏輯
  - 改變了被裝飾對象的類型
  - 會使代碼變得更難理解和調試
  - 當Decorator組合產生負面影響的時候,會增長設計的複雜度
7.4 用State替換狀態改變條件語句
  當控制一個對象狀態轉換的條件表達式過於複雜時,能夠考慮用處理特殊狀態轉換的State類替換條件語句
 
優缺點:
  + 減小或去除狀態改變條件邏輯
  + 簡化了複雜的狀態改變邏輯
  + 提供了觀察狀態改變邏輯的很好的鳥瞰圖
  - 當狀態轉換邏輯已經易於理解的時候,會增長設計的複雜度
7.5 用Composite替換隱含樹
  當用原生表示法隱含的造成了樹結構時,能夠考慮用Composite替換這個原生表示法
 
優缺點:
  + 封裝重複的指令,如格式化、添加或刪除結點
  + 提供了處理類似邏輯增加的通常性方法
  + 簡化了客戶代碼的構造職責
  - 當構造隱式樹更簡單的時候,會增長設計的複雜度
7.6 用Command替換條件調度程序
  當條件邏輯用來調度請求和執行操做時,爲每一個動做建立一個Command。把這些Command存儲在一個集合中,並用獲取及執行Command的代碼替換條件邏輯。
 
  爲每一個動做建立一個Command,把這些Command存儲在一個集合中,並用獲取及執行Command的代碼替換條件邏輯
優缺點:
  + 提供了用統一方法執行不一樣行爲的簡單機制
  + 容許在運行時改變所處理的請求,以及如何處理請求
  + 僅僅須要不多的代碼實現
  - 當條件調度程序已經足夠的時候,會增長設計的複雜度
 

第8章 泛化

  泛化是把特殊代碼轉換成通用目的代碼的過程。泛化代碼的產生每每的重構的結果。
8.1 造成Template Method
  當子類中的兩個方法以相同的順序執行類似的步驟,可是步驟並不徹底相同。經過把這些步驟提取成具備相同簽名的方法來泛化這兩個方法,而後上移這些泛化方法,造成Template Method。
 
優缺點:
  + 經過把不變行爲搬移到超類,去除子類中的重複代碼
  + 簡化並有效的表達了一個通用算法的步驟
  + 容許子類很容易的定製一個算法
  - 當爲了生成算法,子類必須實現不少方法的時候,會增長設計的複雜度
8.2 提取Composite
  當一個類層次結構中的多個子類實現了同一個Composite時,能夠提取一個實現該Composite的超類
 
優缺點:
  + 去除重複的類存儲邏輯和類處理邏輯
  + 可以有效的表達類處理邏輯的可繼承性
8.3 用Composite替換一/多之分
  當類使用不一樣的代碼處理單一對象與多個對象時,用Composite可以產生既能夠處理單一對象又能夠處理多個對象的代碼
 
優缺點:
  + 去除與處理一個或多個對象相關聯的重複代碼
  + 提供處理一個或多個對象的統一方法
  + 支持處理多個對象的更豐富的方法
  - 可能會在Composite的構造過程當中要求類型安全的運行時檢查
8.4 用Observer替換硬編碼的通知
  當子類經過硬編碼來通知另外一個類的實例時能夠去除這些子類,並使其超類可以通知一個或多個實現了Observer接口的類
 
優缺點:
  + 使主題及其觀察者訪問鬆散耦合
  + 支持一個或多個觀察者
  - 當硬編碼的通知已經足夠的時候,會增長設計的複雜度
  - 當出現串聯通知的時候,會增長代碼的複雜度
  - 當觀察者沒有從它們的主題中被刪除的時候,可能會形成資源泄漏
8.5 經過Adapter統一接口
  當客戶代碼與兩個類交互,其中的一個類具備首選接口,能夠用一個Adapter統一接口
 
動機:
  當下麪條件都爲真時,重構Adapter就是有用的:
    兩個類所作的事情相同或類似,可是具備不一樣的接口
    若是類共享同一個接口,客戶代碼會更簡單、更直接、更緊湊
    沒法輕易改變其中一個類的接口,由於它是第三方庫中的一部分,或者它是一個已經被其餘客戶代碼普遍使用的框架的一部分,或者沒法得到源碼
優缺點:
  + 使客戶代碼能夠經過相同的接口與不一樣的類交互,從而去除或減小重複代碼
  + 使客戶代碼能夠經過公共的接口與多個對象交互,從而簡化了客戶代碼
  + 統一了客戶代碼與不一樣類的交互方式
  - 當類的接口能夠改變的時候,會增長設計的複雜度
8.6 提取Adapter
  當一個類適配了多個版本的組件、類庫、API或其餘實體時,能夠爲組件、類庫、API或其餘實體的每一個版本提取一個Adapter
 
  Adapter用來適配對象,Facade用來適配整個系統,Facade一般用來與遺留系統進行交互
優缺點:
  + 隔離了不一樣版本的組件、類庫或API之間的不一樣之處
  + 使類只負責適配代碼的一個版本
  + 避免頻繁的修改代碼
  - 若是某個重要行爲在Adapter中不可用的話,那麼客戶代碼將沒法執行這一重要行爲
8.7 用Interpreter替換隱式語言
  當類中的許多方法組合成了一種隱式語言的元素,能夠爲隱式語言的元素定義類,這樣就能夠經過類實例組合,造成易於理解的表達式
 
優缺點:
  + 比隱式語言更好的支持語言元素的組合
  + 不須要解析新的代碼來支持語言元素的新組合
  + 容許行爲的運行時配置
  - 會產生定義語言和修改客戶代碼的開銷
  - 若是語言很複雜,則須要不少的編程工做
  - 若是語言自己就很簡單,則會增長設計的複雜度

第9章 保護

9.1 用類替換類型代碼
  字段的類型沒法保護它免受不正確的複製和非法的等同性比較,能夠把字段的類型聲明爲類,從而限制複製和等同性比較
 
優缺點:
  + 更好的避免非法賦值和比較
  - 比使用不安全類型要求更多的代碼
9.2 用Singleton限制實例化
  代碼建立了一個對象的多個實例,並致使內存使用過多和系統性能降低時,能夠用Singleton替換多個實例
 
  不要作不成熟的代碼優化,通過不成熟優化的代碼比未優化的代碼更難於重構。在代碼優化以前,你會發現更多能夠改進的地方
優缺點:
  + 改進性能
  - 在任何地方均可以很容易的訪問。在不少狀況下,這多是設計的缺點
  - 當對象含有不能共享的狀態時,本重構無效
9.3 引入Null Object
  當代碼中處處都是處理null字段或變量的重複邏輯時,將null邏輯替換爲一個Null Object,一個提供正確null行爲的對象
 
優缺點:
  + 不須要重複的null邏輯就能夠避免null錯誤
  + 經過最小化null測試簡化了代碼
  - 當系統不太須要null測試的時候,會增長設計的複雜度
  - 若是程序員不知道Null Object的存在,就會產生多餘的null測試
  - 使維護變得複雜,擁有超類的Null Object必須重寫全部新繼承到的公共方法
 

第10章 彙集操做

10.1 將彙集操做搬移到Collecting Parameter
  有一個很大的方法將信息彙集到一個局部變量中時,能夠把結果彙集到一個Collecting Parameter中,並將它傳入被提煉出的方法中
 
優缺點:
  + 幫助咱們把很大的方法轉換成更小的,更簡單的多個方法
  - 使結果代碼運行得更快
10.2 將彙集操做搬移到Visitor
  有一個方法從不一樣的類中 彙集信息,能夠把彙集工做搬移到一個可以訪問每一個類以便採集信息的Visitor中。
 
優缺點:
  + 調節多個算法,使其適用於不一樣的對象結構
  + 訪問相同或不一樣繼承結構中的類
  + 調用不一樣類上的類型特定方法,無需類型轉換
  - 當可使用通用接口把互不相同的類變成類似類的時候,會增長代碼的複雜度
  - 新的可訪問類須要新的接收方法,每一個Visitor中須要新的訪問方法
  - 可能會破壞訪問類的封裝性

第11章 使用重構

11.1 鏈構造函數
  有不少包含重複代碼的構造函數時,能夠把構造函數連接起來,從而得到最少的代碼重複。
11.2 統一接口
  當須要一個與其子類具備相同接口的超類或接口時,能夠找到全部子類含有而超類沒有的公共方法,把這些方法複製到超類中,並修改每一個方法,使其執行空行爲。
11.3 提取參數
  當一個方法或構造函數將一個字段賦值爲一個局部實例化的值時,能夠把賦值聲明的右側提取到一個參數中,並經過客戶代碼提供的參數對字段進行賦值。
相關文章
相關標籤/搜索