《重构-改善既有代码的设计》第三章(上)
生活随笔
收集整理的這篇文章主要介紹了
《重构-改善既有代码的设计》第三章(上)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 前言
- 一、重復代碼(Duplicated Code)
- 二、過長函數(Long Method)
- 三、過大的類(Large Class)
- 四、過長的參數列(Long Parameter List)
- 五、發散式變化(Divergent Change)
- 六、散彈式修改(Shotgun SurGery)
- 七、依戀情結(Feature Envy)
- 八、數據泥團(Data Clumps)
- 九、基本類型偏執(Primitive Obsession)
- 十、switch驚悚現身(Switch Statents)
- 十一、平行繼承體系(Parallel Inheritance Hierarchies)
- 總結
前言
從第三章開始,作者就開始介紹一些重構的具體細節了,通過這些的具體的方法就可以一步一步慢慢開始整個重構的過程了。本章主要講述的主題是代碼的壞味道。所謂代碼的壞味道,我想就是指代碼不好的感覺吧,其實我們很難通過一些量化標準來判定一份代碼是否是好的代碼,但是隨著開發經驗不斷地提升,我們會對不好的代碼更加敏感。本節作者詳細介紹了許多代碼的壞味道,細細品讀,收獲頗豐
一、重復代碼(Duplicated Code)
???????不論是一個剛開始學習編程的人,或是經驗老道的開發者,甚至是僅僅了解編程的外行,都可以理解為什么重復代碼一定是最明顯的壞味道。其中最明顯的問題就是,每一次的修復或是添加新功能,都要同時將所有復制粘貼的地方全部都修改一遍,每疏漏一處,就有可能引入難以追蹤的問題。 ???????重復代碼大致可以分為三種情況,同一個函數有相同表達式;在兩個兄弟的子類有相同表達式;兩個毫不相干的地方有相同的表達式。當重復代碼在同一個函數中,只需要提出一個新的函數;當兄弟子類中,可以考慮將相同的代碼提到父類。如果只有部分不同,完全可以將相同的部分提出,放在父類,再將不同的部分作為抽象方法,由子類實現。(Template Method設計模式)。兩個毫不相關的類,可以把相同部分的提入一個獨立類中,分別調用。二、過長函數(Long Method)
???????在很久以前程序員就已經認識到,程序越長越難理解,往往短小的函數在復用性,拓展性,穩定性都是優于長函數的。在實際生產中,并不能用固定的行數來評定一個函數是否過長,更小的函數可以提供的是更好的理解能力。通過將函數封裝成不同層次的業務邏輯,可以讓開發者無需關心最低層次的底層實現。尤其當不是原作者的開發者來閱讀時會更容易理解。 本節,作者所倡導的原則是:每當感覺要以注釋來說明什么時,我們就把需要說明的東西寫進一個獨立函數中。這樣做的最大好處就是把函數的目的和具體實現層次區分了出來,同一層次的代碼閱讀會比不同層次的更加順暢。 ???????絕大多數只需要使用Extract Method將函數提出即可,但當函數非常長,變量非常多,就會讓你在調用時不斷地進行傳入傳出參數,以及對這些參數的適配處理。這時候就可以使用萬能的殺手锏了。 Replace Method with Method Object 例如: // typescript function longMethod(context) {let paraFor2 = function1(context);let res2 = function2(context, paraFor2);let res3 = function3(context);let res4 = function4(context, paraFor2,res3);return function5(res2, res3,res4) } ???????當我們按照代碼層次不斷提出函數時,可能就會得到示例的函數,層次很清晰,但是像context這個參數就會作為一個臨時變量不斷的傳來傳去,其實在可維護性上是不好的。而我們的殺手锏就是為了處理這個問題,消除這些臨時變量和參數。 // typescript class LongMethod {private _paraFor2;private _res2;private _res3;private _res4;public constructor(private readonly _context) {this._paraFor2 = this._function1();this._res2 = this._function2();this._res3 = this._function3();this._res4 = this._function4();}public function5(){} }let res = new LongMethod(context).function5() ???????因為這只是一個簡單的示例,但足矣說明思路方法。后續可以在此基礎上進行下一步重構,當您真正遇到一個1,2千行的函數,并且絲毫沒有重構思路,嘗試一下,你就可以體會到為什么Replace Method with Method Object是最后的殺手锏了。三、過大的類(Large Class)
???????在實際開發中很容易就會讓一個單個的類做很多事情,尤其是隨著功能的不斷增加。根據類的變量可以從原本不那么科學的結構中提煉到某個組件,如果這個組件適合作為一個子類,那就將它提出為一個子類。過大的類注定導致重復代碼和過多的功能實現。類的職責越多,職責之間或多或少會產生關聯,最終一步步走向混亂。四、過長的參數列(Long Parameter List)
???????在面向對象的編程語言中,幾乎可以不需要過多的參數。過長的參數會使調用者不知道如何使用。甚至還會寫出更多的代碼來為得到參數而處理。當然你也可以運用Preserve Whole Object將同一個對象的數據收集起來,把他們組合成一個參數對象來供你使用。五、發散式變化(Divergent Change)
???????我們是希望軟件容易被修改的,當多個類相互交織時,這種發散式變化就會隨之而來。當我需要對A類的進行修改時,B類的若干個函數也要做相應的適配,反之亦然。針對外界的一次變化,應當都只發生在某一個單一類中,我們應當找出其中相對應的功能,提煉到另一個類之中。六、散彈式修改(Shotgun SurGery)
???????散彈式修改和發散式變化類似,當我們做一次變化時,必須對相應的若干個函數進行修改,這就是散彈式修改。比如為一個枚舉刪除了一個成員,發現所有該枚舉的條件語句都需要隨之修改。此時修改點遍布四處,不但難以尋找,還易于遺漏。七、依戀情結(Feature Envy)
???????對象的要點在于將數據和對數據操作的行為包裝在一起,一個類在操作數據時并不應該對另一個類的數據過分感興趣。當一個類總是在調用另一個類獲取數據的接口時,這就是依戀情結。八、數據泥團(Data Clumps)
???????在實際的開發中你常常可以看到相同的字段,許多函數中相同的參數。其實在函數的使用中或許并不是需要整個大的數據對象。完全可以根據你的實際使用情況來組裝一個新的對象,這時,你就會更清晰地知道哪些數據具體是在哪個類中使用,從而為尋找Feature Envy打下基礎。九、基本類型偏執(Primitive Obsession)
???????開發者往往會清醒于使用基本類型,而不是小型對象。他們會覺得小型對象的封裝和使用是一種增加層次,增加使用復雜。當你嘗試去將相關的字段組合為對象,在很多時候會讓你發現許多類可以改進的地方。你會發現好像這個函數可以放在這個類中,那個應該放在更小的類中。再加以修改,整個代碼就會變得更加有層次。十、switch驚悚現身(Switch Statents)
???????面向對象程序的明顯特征就是,少用switch語句。switch語句最大的問題在于重復,也就是說當你對一個字段的一個操作函數做了switch,往往在這個字段的其他函數中也會有相同的語句。尤其是你不斷使用Extract Method將一些函數提出一個業務層次。大多數時候,switch語句都可以使用多態來替換他,或者轉換成為一些map。在我剛畢業的第一份工作中,我在開發和維護一個十分龐大的C語言系統,其中因為實際業務的復雜性,有若干個分支需要處理。由于C并沒有對象的概念,我看到在整個系統中會初始化一張十分龐大的表,用表的方式之間檢索到相應的具體操作,來避免遍地都是switch。十一、平行繼承體系(Parallel Inheritance Hierarchies)
???????Parallel Inheritance Hierarchies其實是Shotgum Surgery的特殊情況。在這種情況中,每當你給一個類增加一個子類,你就必須為另一個類也增加一個子類,這種重復性互相影響就是平行繼承體系。總結
???????本章的主要以具體詳細的類型來說明哪些是代碼的壞味道,由于篇幅較多,分為上下兩章。上半章中,大部分都不是很需要示例。隨著開發經驗的不斷提高,我發現之前在實際生產中感受到的不好的代碼可以與作者所講述的對應起來。 ???????但是在過長函數的Replace Method with Method Object,我覺得有必要著重分享一下我的想法。曾經有一次,我因為一個近3000行的靜態函數無從下手重構,大致功能就是使用一份數據,然后經過篩選組合成另一份可以填入模板的數據對象。其中復雜的判斷,篩選,組合全部都在同一個函數之中。我通過Extract Method抽出小的函數,結果就成了一堆的靜態函數,一堆的臨時變量,并不能達到我的預期。直到我看到了Replace Method with Method Object,所有的臨時變量,函數參數都可以處理掉,之后整個代碼結構呼之欲出。再通過抽象函數模板將方法的脈絡放在基類,不同的子類去實現不同的篩檢業務,最終得以重構。 ???????代碼的壞味道的每一條都適合去細細品讀,作者在文中的描述會更加全面,并且有更加細致的處理方法。當你一步一步去打磨你的代碼,你就會找到這重構游戲中的樂趣和成就感。總結
以上是生活随笔為你收集整理的《重构-改善既有代码的设计》第三章(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python函数装饰器一篇入魂
- 下一篇: React项目案例-影视资源网站