Refactoring
What Refacing:
重構(Refactoring)就是在不改變軟件現有功能的基礎上,通過調整程序代碼改善軟件的質量、性能,使其程序的設計模式和架構更趨合理,提高軟件的擴展性和維護性。
經典的Refacing Definition:
(1)Refactoring means rewriting existing source code with the intent of improving its design rather than changing its external behavior. The focus of refactoring is on the structure of the source code, changing the design to make the code easier to understand, maintain, and modify. - 來自Borland Together提供的文檔,覺得這個定義很清晰明了。
(2)重構是這樣一個過程:在不改變代碼外在行為的前提下,對代碼做出修改,已改進程序的內部結構。-來自Martin Fowler的定義。
Refactoring (noun): a change made to the internal structure of software to make iteasier to understand andcheaper to modifywithout changing its observable behavior.
Refactor (verb): to restructure softwareby applying a series of refactoringswithout changing its observable behavior.
Why Refacing:
Improves the design of software
Makes software easier to understand - leads to higher levels of understanding
Helps to find bugs
Helps to be more effective at writing robust code
Helps to program faster
1、·持續偏糾和改進軟件設計。
2、·使代碼更易為人所理解。
3、·幫助發現隱藏的代碼缺陷。
4、·從長遠來看,有助于提高編程效率。
When Refacing:Three strikes and you refactor.
When you add functionality.
When you need to fix a bug.
When you do a code review.
1、·代碼中存在重復的代碼。
2、·過大的類和過長的方法。
3、·牽一毛而需要動全身的修改。
4、·類之間需要過多的通訊。
5、·過度耦合的信息鏈。
6、·不完美的設計。
7、·缺少必要的注釋。
不要當你需要新的功能的時候,或者原來的程序出了Bug,你才想起重構。
總結:代碼重構在你開始寫代碼就是被要考慮進去的,邊寫代碼邊重構。
What makes programs hard to work with?
Kent Beck:
We want programs:
easy to read,
have all logic specified in one and only place,
do not allow changes to endanger existing behavior,
allow conditional logic to be expressed as simply as possible.
Refactoring and Design
Refactoring and Performance
Refactoring will make software run more slowly, but it also makes the software more easy for performance tuning. The secret to fast software is towrite tunable software first andthen to tune it for sufficient speed.
Run a profiler to find the performance hot spots.
Focus on these performance hot spots and optimize them.
Rerun the profiler, if the performance does not improve, back out the change.
Continue the process until the performance satisfies your users.
How Refacing:
減少代碼壞味道(Bad Smell in Codes)的重構策略
1.Duplicated Code 盡量消除重復的代碼,將它們合而為一 Do Not Repeat Yourself.
根據重復的代碼出現在不同的地方,分別采取不同的重構的策略:
在同一個Class的不同地方:通過采用重構工具提供的Extract Method功能提煉出重復的代碼, 然后在這些地方調用上述提煉出方法。
在不同Subclasses中:通過Extract Method提煉出重復的代碼,然后通過Pull Up Method將該方法移動到上級的Super class內。
在沒有關系的Classes中:通過對其中一個使用Extract Class將重復的代碼提煉到一個新類中,然后在另一個Class中調用生成的新類,消除重復的代碼。
?
2.Long Method 拆解過長的函數
過長的函數在我們的日常代碼中經??梢?#xff0c;在C#中常通過#region #endregion區隔為不同的功能區域。
重構策略:通過Extract Method將過長的函數按照功能的不同進行適當拆解為小的函數,并且給這些小函數一個好名字。通過名字來了解函數提供的功能,提高代碼的理解性。
?
3.Large Class 拆解過大的類
過大的類也經常見到,特別是類中含有大量的成員變量。
重構策略:通過Extract Class將一些相關成員變量移植到新的Class中,如Employee類,一般會包含有聯系方式的相關屬性(電話, Mobile,地址,Zip等等),則可以將這些移植到新的EmployeeContact類中。
?
4.Long Parameter List 過長的參數列
過長的參數列的主要問題是難以理解,并且難以維護。如果要增加新的參數或者刪除某一參數,易造成參數前后不一致。
重構策略:如果可以通過向已存在的對象查詢獲取參數,則可通過Replace Parameter with Method,移除參數列,通過在函數內部向上述已存在的對象查詢來獲取參數。
如果參數列中若干參數是已存在對象的屬性,則可通過Preserve Whole Object將這些參賽替換為一個完整對象,這樣不僅提高代碼的可讀性,同時已易于代碼今后的維護。
另外,還可以將若干不相關的參數,使用Introduce Parameter Object來創建一個新的參數類。不過,我個人覺得如果這些情況過多的話,會產生很多莫名其妙的參數類了,反而降低代碼的可讀性。
5.Divergent Change(發散式變化)
現象:當某個Class因為外部條件的變化或者客戶提出新的功能要求等時,每次修改要求我們更新Class中不同的方法。不過這種情況只有在事后才能覺察到,因為修改都是在事后發生的么(廢話)。
重構策略:將每次因同一條件變化,而需要同時修改的若干方法通過Extract Class將它們提煉到一個新Class中。實現目標是:每次變化需要修改的方法都在單一的Class中,并且這個新的Class內所有的方法都應該與這個變化相關。
?
6.Shotgun Surgery(霰彈式修改)
現象:當外部條件發生變化時,每次需要修改多個Class來適應這些變化,影響到很多地方。就像霰彈一樣,發散到多個地方。
重構策略:使用Move Method和Move Field將Class中需要修改的方法及成員變量移植到同一個Class中。如果沒有合適的Class,則創建一個新Class。實現目標是,將需要修改的地方集中到一個Class中進行處理。
?
比較Divergent Change(發散式變化)和Shotgun Surgery(霰彈式修改):
前者指一個Class受到多種外部變化的影響。而后者指一種變化需要影響到多個Class需要修改。都是需要修理的對象。
?
7.Feature Envy(依戀情結)
現象:Class中某些方法“身在曹營心在漢”,沒有安心使用Class中的成員變量,而需要大量訪問另外Class中的成員變量。這樣就違反了對象技術的基本定義:將數據和操作行為(方法)包裝在一起。
重構策略:使用Move Method將這些方法移動到對應的Class中,以化解其“相思之苦”,讓其牽手。
?
8.Data Clumps(數據泥團)
現象:指一些相同數據項目(Data Items),如Class成員變量和方法中參數列表等,在多個Class中多次出現,并且這些數據項目有其內在的聯系。
重構策略:通過使用Introduce Parameter Object(創建新的參數對象取代這些參數)或Preserve Whole Object(使用已存在的對象取代這些參數),實現使用對象代替Class成員變量和方法中參數列表,清除數據泥團,使代碼簡潔,也提高維護性和易讀性。
?
9.Primitive Obsession(基本型偏執狂)
現象:在Class中看到大量的基本型數據項目(Data Item),如Employee類中有大量的數據成員,Employee#, FirstName, MiddleName, LastName, Address, State, City, Street, Zip, OfficePhone, CellPhone, Email……等等。
重構策略:使用Extract Class(提煉新類)或Preserve Whole Object(使用已存在的對象取代這些參數),實現使用對象代替基本型數據項目(Data Item)。如上述Employee類中就可分別提煉出EmployeeName和EmployeeContact兩個新類。
?
10.Switch Statements(Switch語句)
現象:同樣的Switch語句出現在不同的方法或不同的Class中,這樣當需要增加新的CASE分支或者修改CASE分支內語句時,就必須找到所有的地方,然后進行修改。這樣,就比較麻煩了。
重構策略:(1)首先采用Extract Method將Switch語句提煉到一個獨立的函數。
(2)然后以Move Method搬移到需要多態性(Polymorphism)的Superclass里面或者是構建一個新的Superclass。
(3)進一步使用Replace Type Code with Subclasses或者Replace Type Code with State/Strategy。這步就比較麻煩些,不過記住如下基本規則:這里一般有3個Class分別為Source Class、Superclass和Subclass。
Source Class:
l???????? 使用Self Encapsulate Field,將Type Code成員變量封裝起來,也就是建立對應的Setter/Getter函數。
l???????? 在Source Class中增加一個Superclass類型的成員變量,用來存放Subclass實例對象。
l???????? 在Source Class中的Getter函數,通過調用Superclass的Abstract Query函數來完成。
l???????? 在Source Class中的Setter函數,通過調用Superclass中的Static工廠化方法來獲取合適的Subclass實例對象。
?
Superclass:
新建的一個Class(注:就是上面通過Move Method搬移生成的Superclass),根據Type Code的用途命名該Class,作為Superclass。
l???????? 在Superclass中建立一個Abstract Query函數,用來獲取Subclass的Type Code。
l???????? 在Superclass中創建Static工廠化方法生產對應的Subclass對象,這里會存在一個Switch語句(不要再動腦筋來重構這個Switch語句了,這個Switch語句不會在多處重復存在,并且這里用于決定創建何種Subclass對象,這是完全可以接受的)。
?
Subclass:
l???????? 根據每一個Switch/Type分支,建立對應的Subclass,并且Subclass的命名可以參考Switch/Type分支的命名。
l???????? 在每一個Subclass中重載Superclass的Abstract Query函數,返回特定的Type Code。
(4)現在Superclass仍然存在Switch分支,是時候輪到Replace Conditional with Polymorphism上場了。具體而言,就是在每一個Subclass中創建重載方法(注:該方法是Superclass中含有Switch語句的方法),并將Superclass中Switch語句對應的Case分支剪切過來。最后將Superclass中該方法初象化Abstract,并清除Switch語句及其所有的Case分支。
這樣就完成了整個重構過程,這個比較麻煩。
11.Parallel Inheritance Hierarchies(平行繼承體系)
現象:為某個class增加一個subclass時,也必須為另一個class相應增加一個subclass。重構策略: 在一個class繼承體系的對象中引用(refer to)另一個class繼承體系的對象,然后運用Move Method和Move Field將被引用class中的一些方法和成員變量遷移宿主class中,消除被引用class的繼承體系(注:這種平行繼承體系好象比較少見也)。
?
12.Lazy Class(冗贅類)
現象:某一些class由于種種原因,現在已經不再承擔足夠責任,有些多余了。如同國有企業冗余人員一樣,需要下崗了。
重構策略:通過Collapse Hierarchy,將這些冗余的class合并到superclass或subclass中,或者通過Inline Class(與Extract Class相反),將這些冗余class中的所有Method/Field遷移到其他相關的class中。
?
13.Speculative Generality(夸夸其談未來性)
現象:系統中出現一些無用的abstract class,或者非必要的delegation(委托),或者多余的參數等等。
重構策略:分別使用Collapse Hierarchy合并abstract class,使用Inline Class移除非必要的delegation,使用Remove Parameter刪除多余的參數。
?
14.Temporary Field(令人迷惑的暫時值域)
現象:class中存在一些Field,這些Field只在某種非常特定的情況下需要。
重構策略:通過Extract Class將這些孤獨的Field及其相關的Method移植的一些新的Class中。提煉出來的新Class可能沒有任何抽象意義,只是提供Method的調用,這些新Class一般稱為Method Object。
?
15.Message Chains(過度耦合的消息鏈)
現象:向一個對象請求另一個對象,然后再向后者請求另一個對象,……,這就是Message Chain,意味著Message Chain中任何改變,將導致Client端不得不修改。
重構策略:通過Hide Delegate(隱藏委托關系)消除Message Chain,具體做法是在Message Chain的任何地方通過Extract Method建立一個簡單委托(Delegation)函數,來減少耦合(Coupling)。
?
16.Middle Man(中間轉手人)
現象:過度運用delegation,某個/某些Class接口有一半的函數都委托給其他class,這樣就是過度delegation。
重構策略:運用Remove Middle Man,移除簡單的委托動作(也就是移除委托函數),讓client直接調用delegate受托對象。和上面的Hide Delegate(隱藏委托關系)剛好相反的過程。
?
由于系統在不斷的變化和調整,因此[合適的隱藏程度]這個尺度也在相應的變化,Hide Delegate和Remove Middle Man重構策略可以系統適應這種變化。
?
另外,可保留一部分委托關系(delegation),同時也讓Client也直接使用delegate受托對象。
?
17.Inappropriate Intimacy(狎昵關系)
現象:兩個Class過分親密,彼此總是希望了解對方的private成分。
重構策略:可以采用Move Method和Move Field來幫助他們劃清界限,減少他們之間親密行為。或者運用Change Bidirectional Association to Unidirectional,將雙向關聯改為單向,降低Class之間過多的依存性(inter-dependencies)。或者通過Extract Class將兩個Class之間的共同點移植到一個新的Class中。
?
18.Alternative Classes with Different Interfaces(異曲同工的類)
現象:兩個函數做相同的事情,卻有不同的signature。
重構策略:使用Rename Method,根據他們的用途來重命名。另外,可以適當運用Move Method遷移某些行為,使Classes的接口保持一致。
?
19.Incomplete Library Class(不完美的程序庫類)
現象:Library Class(類庫)設計不是很完美,我們需要添加額外的方法。
重構策略:如果可以修改Library Class的Source Code,直接修改最好。如果無法直接修改Library Class,并且只想修改Library Class內的一兩個函數,可以采用Introduce Foreign Method策略:在Client Class中建立一個函數,以外加函數的方式來實現一項新功能(一般而言,以server class實例作為該函數的第一個參數)。
?
如果需要建立大量的額外函數,可應該采用Introduce Local Extension:建立一個新class,使它包含額外函數,并且這個class或者繼承或者wrap(包裝)source class。
?
20.Data Class(純稚的數據類)
現象:Data Class指:一些Class擁有Fields,以及用來訪問Fields的getter/setter函數,但是沒有其他的功能函數。(感覺這些Data Class如同Entity Class或Parameter Class,用來傳遞參數,我認為這種情況下沒有必要重構。)
重構策略:找出其他class中訪問Data Class中的getter/setter的函數,嘗試以Move Method將這些函數移植到Data Class中,實現將數據和操作行為(方法)包裝在一起,也讓Data Class承擔一定的責任(方法)。
?
21.Refused Bequest(被拒絕的遺贈)
現象:Subclass不想或不需要繼承superclass的部分函數和Field。
重構策略:為subclass新建一個兄弟(sibling class),再運用Push Down Method和Push Down Field將superclass中的相應函數和Field下推到兄弟class,這樣superclass就只包含subclass共享的東西了。其實,也就是將superclass中一些與特定的函數和Field放到特定的subclass中,superclass中僅包含subclass共享的函數和Field。
?
如果不想修改superclass,還可以運用Replace Inheritance with Delegation來達到目的。也就是以委托取代繼承,在subclass中新建一個Field來保存superclass對象,去除subclass對superclass的繼承關系,委托或調用superclass的方法來完成目的。
?
22.Comments(過多的注釋)
現象:(暈倒,這個也要重構,Remove掉所有的Comments嗎?不是。)當代碼中出現一段長長的注釋,一般是由于代碼比較糟糕,需要進行重構,除去代碼的壞味道。
重構策略:通過上面提及的各種重構策略,將代碼的壞味道去除,使注釋變成多余。
如果需要注釋/解釋一段代碼做了什么,則可以試試Extract Method,提取出一個獨立的函數,讓函數名稱解釋該函數的用途/功能。另外,如果覺得需要注釋來說明系統的某些假設條件,
也可嘗試使用Introduce Assertion(引入斷言),來明確標明這些假設。
?
當你感覺需要撰寫注釋時,請先嘗試重構,試著讓所有的注釋都變得多余。?
注:并不是一看到Switch語句及CASE分支,就馬上/偏執狂采用上述重構策略進行重構,畫蛇添足或吃虧不討好(個人觀點)。一般而言,只有看到多處出現相同的Switch語句時,才應該考慮進行重構。
Refactoring Practice:
1、When you find you have to add a feature to a program, and the program's code is not structured in a convenient way to add the feature, first refactor the program to make it easy to add the feature, then add the feature.
2、Before you start refactoring, check that you have a solid suite of tests. These tests must be self-checking.
3、Refactoring changes the programs in small steps. If you make a mistake, it is easy to find the bug.
4、Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
5、Don't publish interfaces prematurely. Modify your code ownership policies to smooth refactoring.
6、Make sure all tests are fully automatic and that they check their own results.
7、A suite of tests is a powerful bug detector that decapitates the time it takes to find bugs.
8、Run your tests frequently. Localize tests whenever you compile—every test at least every day.
9、When you get a bug report, start by writing a unit test that exposes the bug.
10、It is better to write and run incomplete tests than not to run complete tests.
11、Think of the boundary conditions under which things might go wrong and concentrate your tests there.
12、Don't forget to test that exceptions are raised when things are expected to go wrong.
13、Don't let the fear that testing can't catch all bugs stop you from writing the tests that will catch most bugs.
1、把握全局,先了解業務
2、閱讀代碼,清除冗余代碼,重構變量名等,刪除不一致或無效的注釋,補寫注釋,為下一步做準備
3、將通用的代碼塊提升為方法
4、小步前進,隨時測試
5、重構要隨時進行
6、避免過度設計。模式是把雙刃劍。
最終的收獲:每個程序員都應該不斷加強自己的編程思想,注重編程質量。
1、 管理層不重視代碼書寫,認為是體力勞動;
2、 項目經理疲于應付進度,無心且無力;
3、 程序員水平參差不齊,缺乏正確的指導。
4、.經驗不足,分析設計不到位;
5、.敏捷開發,雖然經驗很多,但為了快速開發,沒有經過分析設計;
6、.缺乏意識,只為實現功能而寫代碼,不管代碼質量;
About Refacing:
重構工作雖然重要,但是得不到過多的認可,目前國內關注的是可用性,對于代碼質量并沒有得到應有的重視。
Refacing Books:
《work effectively with legacy code》 修改代碼的藝術
《The Programtic Programmer From JoumeyMan to Master》 程序員修煉之道
《Pattern-Oriented Software Architecture Volume 4》 面向模式的軟件架構 卷4
《Agile Principles、Patterns and Practice in C#》 敏捷軟件開發 原則、模式與實踐(C#版)
《Code Quality The Open Source Perspective》 高質量程序設計藝術
《Refactoring improving the Designe of Existing Code》? 重構 改善既有代碼的設計
《Design Patterns Explained 》 設計模式解析
《反模式 危機中軟件、架構和項目的重構》
《Refactoring to Patterns》 重構與模式
《More Programming Pearls》 編程珠璣II
《Programming Pearls》 編程珠璣(第2版)
《Beginning Java Objects》 中文版:從概念到代碼(第2版)
《設計模式解析(第2版)》
《敏捷軟件開發:原則、模式與實踐(C#版)》
《Java設計模式》
《重構與模式》
《UML面向對象建模與設計(第2版)》
轉載于:https://www.cnblogs.com/yefengmeander/archive/2011/08/18/2887695.html
總結
以上是生活随笔為你收集整理的Refactoring的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QT VS环境安装后出现生成的程序can
- 下一篇: Mysql数据库课程设计