標籤:軟體 架構 重構
軟體演化的類型
軟體演化就像生物進化一樣,有些突變對物種是有益的,而有些是有害的。
區分軟體演化類型的關鍵,就是程式品質在這一過程中時提高了還是降低了。其二,就是這樣的演化是源於程式構建過程中的秀海,還是維護過程中的修改。
重構簡介
要實現軟體演化基本準則,最關鍵的策略就是重構。
重構的理由
1、代碼重複,重複的代碼幾乎是代表著最初設計裡徹底分解方面的一個事物。無論何時,如果需要對某個地方進行修改,你都不得不在另一個地方完成這樣的修改——重複代碼總會將你置於一種兩線作戰的尷尬境地。
2、冗長的子程式,改善方法之一就是提升其模組性。
3、迴圈過長或嵌套過深
4、內聚性太差的類,如果有某個類中寫了很多彼此無關的東西,那麼這個類就應該被拆分成多個類,每個類負責一組具有內在的互相關聯的任務。
5、類的介面未能提供層次一致的抽象。
6、擁有太多參數的參數列表
7、類的內部修改往往被局限某個部分,變化導致多個類的相同修改,如果發現常常對同一組類進行修改,這表明這些類中的代碼應當被重新組織,使修改僅影響到其中的一個類。
8、對繼承體系的同樣修改,每次為某個類添加衍生類別時,都會發現自己不得不對另一個類做同樣的操作。這就是一種特殊的相同修改,應該避免這種情況。
9、同時使用的相關資料並未以類的方式進行組織,如果常常對同樣一組資料進行操作,應當想想是否應該將這些資料及其操作組織到一個類裡面。
10、成員函數使用其他類的特徵比使用自身類的特徵還要多,這就暗示了這一子程式應該被放到另一個類中區,然後再原來的類裡調用。
11、過多使用基礎資料型別 (Elementary Data Type)
12、某個類無所事事,有時代碼的重構會導致某個已有的類無事可做。如果一個類看起來名不副實,那麼就應該考慮是否將該類的功能轉交給其他的類,然後徹底刪除這個類。
13、一系列傳遞資料的子程式
14、中間人對象無事可做,如果某個類中的絕大部分代碼只是去調用其他類中的成員函數,請考慮是否把這樣的中間人去掉,轉而直接調用其他類。
15、某個類同其他類過於親密,如果需要使程式具備更強的可管理性,並最大限度地減少更待代碼周圍的連帶影響,那麼(資訊隱藏)可能是最強有力的工具了。
16、子程式命名不當,只要看到此問題就應立刻修改。
17、資料成員被設定為公用,請認真考慮吧public資料成員藏在訪問器子程式背後。
18、某個衍生類別僅使用基類的很少一部分成員函數,通常,這種情況表明這一類衍生類別的建立僅僅是由於基類碰巧有了該類所需要的子程式,而不是出於邏輯上的派生關係。因此應當考慮進行更完善的封裝:把衍生類別相對於基類的關係從"is-a"轉變為"has-a"。
19、注釋被用於難懂的代碼,不要為了拙劣的代碼編寫文檔,應當重寫代碼。
20、使用了全域變數
21、程式中的一些代碼似乎是在將來的某個時候才會用到的,這樣的超前設計應該盡量避免,除非想的相當周到(一般不會出現)。對未來需求有所準備的辦法並不是去編寫空中樓閣式的代碼,而是儘可能將滿足當前需求的代碼清晰直白地表現出來,使未來的程式員能夠理解這個程式的意圖。
特定的重構
資料級的重構
1、用具名常量代替神秘數值
2、是變數名字跟加清晰傳遞出更多資訊
3、將運算式內連化
4、用函數來代替運算式
5、引入中間變數
6、用多個單一用途變數代替某個多用途變數
7、在局部用途中使用局部變數而不是參數
8、將基礎類型資料轉化為類
9、將一群組類型碼轉化為類或枚舉類型
10、將一群組類型碼轉換為一個基類及其相應的衍生類別
11、將數群組轉換為對象
12、把群集封裝起來
13、用資料類來代替傳統記錄
語句級的重構
1、分解布林運算式
2、將複雜布林運算式轉換成命名準確的布爾函數
3、合并條件陳述式不通部分的重複程式碼片段
4、使用break或return而不是迴圈變數來退出
5、在嵌套的if-else語句中一旦知道答案就立即返回,而不是去賦一個返回值
6、用多態來覆寫準則語句
7、建立和使用null對象而不是去檢測空值
子程式級重構
1、提取子程式或方法
2、將子程式的代碼內連化
3、將冗長的子程式轉換為類
4、用簡單演算法替代複雜演算法
5、增加參數
6、刪除參數
7、將查詢操作從修改操作中獨立出來
8、合并相似的子程式,通過參數區分它們的功能
9、將行為取決於參數的子程式拆分開來
10、傳遞整個對象而非特性成員
11、傳遞特性成員而非整個對象
12、封裝向下轉型的操作
類實現的重構
1、將值對象轉化為引用對象,如果返現建立並維護著多個一模一樣的大型複雜物件,請改變對象的使用方式,僅僅保留一份拷貝,然後在其他地方引用它。
2、將引用對象轉化為值對象,如果自己某個小型簡單對象進行了多次引用操作,請將這些對象都設為值對象。
3、用資料初始化代替虛函數
4、改變成員函數或成員資料的位置,將資料移到基類中,將子函數移動到基函數中
5、將特殊代碼提取為衍生類別
6、將相似的代碼結合起來放置到基類中,如果兩個衍生類別有相似的代碼,將這些代碼結合起來並放到基類中。
類介面的重構
1、將成員函數放到另一個類中
2、將一個類變成兩個
3、刪除類
4、去除委託關係
5、去掉中間人
6、用委託代替繼承
7、用繼承代替委託
8、引入外部的成員函數
9、引入擴充類
10、對暴露在外的成員變數進行封裝
11、對於不能修改的類成員,刪除相關的set()成員函數
12、隱藏那些不會在類之外被用到的成員函數
13、封裝不適用的成員函數
14、合并那些實現非常類似的基類和衍生類別
系統級重構
1、為無法控制的資料建立明確的索引源
2、想單向的類聯絡改為雙向類聯絡,如果兩個類,且他們各自需要用到對方的功能,但僅有一個類能訪問另一個類。這時就應該將對兩個類進行修改,使其能互相調用。
3、將雙向的類聯絡改為單向聯絡,如果兩個類,彼此都知道對方,但實際上只有一個類需要訪問另一個類,此時就需要這種變換。
4、用異常取代錯誤處理代碼或者相反方向變換,這取決與錯誤處理的策略。
安全的重構
重構是一種改善代碼品質的強有力技術,但是使用不當,重構會帶來很多麻煩。下面是一些小建議:
1、儲存初始代碼:防止改錯了無法複原
2、重構的步伐應該小一些
3、同一時間只做一項重構
4、把要做的事情一條條列出來
5、設定一個停車場,在某次重構的過程中,可能會發現需要進行另一次重構。正在著手這次新的重構時,擷取又發現第三個重構將會給程式帶來很多好處。為了處理這些並不重要立即對付的修改工作,最好設定一個“停車常”,把需要在未來某個時間進行而現在可以放在一邊的修改工作列出來。
6、多使用還原點,在重構的時候,很容易出現代碼沒有按照設想正常啟動並執行情況。除了儲存初始代碼外,在重構中還應該在多個地方設定還原點。這樣可以如果改的程式無法運行,可以恢複到之前可以啟動並執行狀態。
7、利用編譯器警告資訊
8、重新測試
9、增加測試案例
10、檢查對代碼的修改
11、根據重構風險層級來調整重構方法
重構策略
1、在增加子程式時進行重構
2、在添加類的時候進行重構
3、在修補缺陷的時候進行重構
4、關注易於出錯的模組
5、關注高度複雜的模組
6、在維護環境下,改善手中正在處理的代碼
7、定義清楚乾淨代碼和拙劣代碼之間的邊界,然後嘗試把代碼移過這條邊界
軟體架構————重構