重構——代碼的壞味道

1. Duplicated Code(重複的代碼)程序員

臭味行列中首當其衝的就是Duplicated Code。若是你在一個以上的地點看到相同的程序結構,那麼當可確定:設法將它們合而爲一,程序會變得更好。算法

最單純的Duplicated Code就是[同一個class內的兩個函數含有相同表達式(expression)]。這時候你須要作的就是採用Extract Method提煉出重複的代碼,而後讓這兩個地點都調用被提煉出來的那一段代碼。數據庫

另外一種常見狀況就是[兩個互爲兄弟(sibling)的subclasses內含有相同表達式]。要避免這種狀況,只須要對兩個classes都使用Extract Method,而後再對被提煉出的代碼使用Pull Up Method,將它推入superclass內。若是代碼之間只是相似,並不是徹底相同,那麼就得運用Extract Method將類似部分和差別部分割開,構成單獨一個函數。而後你可能發現或許能夠運用Form Template Method得到一個Template Method設計模式。若是有些函數以不一樣的算法作相同的事,你能夠擇定其中較清晰的一個,並使用Substitute Algorithm將其它函數的算法替換掉。express

若是兩個絕不相關的classes內出現Duplicated Code,你應該考慮對其中一個使用Extract Class,將重複代碼提煉到一個獨立class中,而後在另外一個class內使用這個新class。可是,重複代碼所在的函數也可能的確只應該屬於某個class,另外一個class只能調用它,抑或這個函數可能屬於第三個class,而另兩個classes應該引用這第三個class。你必須決定這個函數放在哪兒最合適,並確保它被安置後就不會再在其它任何地方出現。編程

2. Long Method(過長函數)設計模式

擁有[短函數](short methods)的對象會活得比較好、比較長。不熟悉面向對象技術的人,經常以爲對象程序中只有無窮無盡的delegation(委託),根本沒有進行任何計算。和此類程序共同生活數年以後,你纔會知道,這些小小函數有多大價值。[間接層]所能帶來的所有利益——解釋能力、共享能力、選擇能力——都是由小型函數支持的。安全

好久之前程序員就已認識到:程序愈長愈難理解。早期的編程語言中,[子程序調用動做]須要額外開銷,這使得作大家不太樂意使用small method,現代OO語言幾乎已經徹底免除了進程內的[函數調用動做額外開銷]。不過代碼閱讀者仍是得多費力氣,由於他必須常常轉換上下文去看看子程序作了什麼。某些開發環境容許用戶同時看到兩個函數,這能夠幫助你省去部分麻煩,可是讓small method容易理解的真正關鍵在於一個好名字。若是你能給函數起個好名字,讀者就能夠經過名字瞭解函數的做用,根本沒必要去看其中寫了些什麼。session

最終的效果是:你應該更積極進取地分解函數。咱們遵循這樣一條原則:每當感受須要以註釋來講明點什麼的時候,咱們就把須要說明的東西寫進一個獨立的函數中,並以其用途(而非實現手法)命名。咱們能夠對一組或甚至短短一行代碼作這件事。哪怕替換後的函數調用動做比函數自身還長,只要函數名稱可以解釋其用途,咱們也該絕不猶豫地那麼作。關鍵不在於函數的長度,而在於函數[作什麼]和[如何作]之間的語義距離。併發

百分之九十九的場合裏,要把函數變小,只需使用Extract Method。找到函數中適合集在一塊兒的部分,將它們提煉出來造成一個新函數。app

若是函數內有大量的參數和臨時變量,它們會對你的函數提煉造成阻礙。若是你嘗試運用Extract Method,最終就會把許多這些參數和臨時變量看成參數,傳遞給被提煉出來的新函數,致使可讀性幾乎沒有任何提高。啊是的,你能夠常常運用Replace Temp with Query則能夠將過長的參數列變得更簡潔一些。

若是你已經這麼作,仍然有太多臨時變量和參數,那就應該拿出咱們的殺手鐗:Replace Method with Method Object。

如何肯定該提煉哪一段代碼呢?一個很好的技巧是:尋找註釋。它們一般是指出[代碼用途和實現手法間的語義距離]的信號。若是代碼前言有一行註釋,就是在提醒你:能夠將這段代碼替換成一個函數,並且能夠在註釋的基礎上給這個函數命名。就算只有一行代碼,若是它須要以註釋來講明,那也值得將它提煉到獨立的函數去。

條件式和循環經常也是提煉的信號。你可使用Decompose Conditional處理條件式。至於循環,你應該將循環和其內的代碼提煉到一例獨立函數中。

3. Large Class(過大類)

若是想利用單一class作太多事情,其內每每就會出現太多instance變量。一旦如此,Duplicated Code也就接踵而至了。

你能夠運用Extract Class將數個變量一直提煉到新class內。提煉時應該選擇class內彼此相關的變量,將它們放在一直。例如」depositAmount」和」depositCurrency」可能應該隸屬同一個class。一般若是class內的數個變量有着相同的前綴或字尾,這就意味有機會把它們提煉到某個組件內。若是這個組件適合做爲一個subclass,你會發現Extract Subclass每每比較簡單。

有時候class並不是在全部時刻都使用全部instance變量。果然如此,你或許能夠屢次使用Extract Class或Extract Subclass。

和[太多instance變量]同樣,class內若是有太多代碼,也是[]代碼重複、混亂、死亡]的絕佳滋生地點。最簡單的解決方案是把贅餘的東西消弭於class內部。若是有五個[百行函數],它們之中不少代碼都相同,那麼或許你能夠把它們變成五個[十行函數]和十個提煉出來的[雙行函數]。

和[擁有太多instance變量]同樣,一個class若是擁有太多代碼,每每也適合使用Extract Class和Extract Subclass。這裏有個有用技巧:先肯定客戶端如何使用它們,而後運用Extract Interface爲每一種使用一個接口。這或許能夠幫助你看清楚如何分解這個class。

若是你的Large Class是個GUI class,你可能須要把數據和行爲移到一個獨立的領域對象去。你可能須要兩邊各保留一些重複數據,並令這些數據同步。Duplicate Observed Data告訴你該怎麼作。這種狀況下,特別是若是你使用舊式AWT組件,你能夠採用這種方式去掉GUI class並代以Swing組件。

4. Long Parameter List(過長參數列)

剛開始學習編程的時候,老師教咱們:把函數所需的全部東西都以參數傳遞進去。這能夠理解,由於除此以外就只能選擇全局數據,而全局數據是邪惡的東西。對象技術改變了這一狀況,由於若是你手上沒有你所須要的東西,總能夠叫另外一個對象給你。所以,有了對象,你就沒必要把函數須要的全部東西都以參數傳遞給它了,你只需給它足夠的東西、讓函數能從中得到本身須要的全部東西就好了。函數須要的東西多半能夠在函數的宿主類(host class)中找到。面向對象程序中的函數,其參數列一般比在傳統程序中短得多。

這是好現象,由於太長的參數列難以理解,太多參數會形成先後不一致、不易使用,並且一旦你須要更多數據,就不得不修改它。若是將對象傳遞給函數,大多數修改都將沒有必要,由於你極可能只需(在函數內)增長一兩條請求,就能獲得更多數據。

若是[向既有對象發出一條請求]就能夠取得本來位於參數列上的一份數據,那麼你應該激活重構準則Replace Parameter with Method。上述的既有對象多是函數所屬class內的一個值域,也多是另外一個參數。你還能夠運用Preserve Whole Object未來自同一對象的一堆數據收集起來,並以該對象替換它們。若是某些數據缺少合理的對象歸屬,可以使用Introduce Parameter Object爲它們製造出一個[參數對象]。

此間存在一個重要的例外。有時候你明顯不但願形成[被調用之對象]與[較大對象]間的某種依存關係。這時候將數據從對象中拆解出來單獨做爲參數,也很合情合理。可是請注意其所引起的代價。若是參數列太長或變化太頻繁,你就須要從新考慮本身的依存結構了。

5. Divergent Change(發散式變化)

咱們但願軟件可以更容易被修改——畢竟軟件再怎麼說原本就該是[軟]的。一旦須要修改,咱們但願可以跌到系統的某一點,只在該處作修改。若是不能作到這點,你就嗅出兩種緊密相關的刺鼻味道中的一種了。

若是某個class常常由於不一樣的緣由在不一樣的方向上發生變化,Divergent Change就出現了。當你看着一個class說:「呃,若是新加入一個數據庫,我必須修改這三個函數;若是新出現一種金融工具,我必須修改這四個函數」,那麼此時也許將這個對象分紅兩個會更好,這麼一來每一個對象就能夠只因一種變化而須要修改。固然,每每只有在加入新數據庫或新金融工具後,你才能發現這一點。針對某一外界變化的全部相應修改,都只應該發生在單一class中,而這個新class內的全部內容都應該反應該外界變化。爲此,你應該找出因着某特定緣由而形成的全部變化,而後運用Extract Class將它們提煉到另外一個class中。

6. Shotgun Surgery(霰彈式修改)

Shotgun Surgery相似Divergent Change,但偏偏相反。若是每遇到某種變化,你都必須在許多不一樣的class內作出許多小修改以響應之,你所面臨的壞味道就是Shotgun Surgery。若是須要修改的代碼散佈四處,你不但很難找到它們,也很容易忘記某個重要的修改。

這種狀況下你應該使用Move Method和Move Field把全部須要修改的代碼放進同一個class。若是眼下沒有合適的class能夠安置這些代碼,就創造一個。一般你能夠運用Inline Class把一系列相關行爲放進同一個class。這可能會形成少許Divergent Change,但你能夠輕易處理它。

Divergent Change是指[一個class受多種變化的影響],Shotgun Surgery則是指[一種變化引起多個classes相應修改]。這兩種狀況下你都會但願整理代碼,取得[外界變化]與[待改類]呈現一對一關係的理想境地。

7. Feature Envy(依戀情結)

對象技術的所有要點在於:這是一種[將數據和加諸其上的操做行爲包裝在一塊兒]的技術。有一種經典氣味是:函數對某個class的興趣高過對本身所處之host class的興趣。這種孺慕之情最一般的焦點即是數據。無數次經驗裏,咱們看到某個函數爲了計算某值,從另外一個對象那兒調用幾乎半打的取值函數。療法顯而易見:把這個函數移到另外一個地點。你應該使用Move Method把它移到它該去的地方。有時候函數中只有一部分受這種依戀之苦,這時候你應該使用Extract Method把這一部分提煉到獨立函數中,再使用Move Method帶它去它的夢中家園。

固然,並不是全部狀況都這麼簡單。一個函數每每會用上數個classes特性,那麼它究竟該被置於何處呢?咱們的原則是:判斷哪一個class擁有最多[被此函數使用]的數據,而後就把這個函數和那些數據擺在一塊兒。若是先以Extract Method將這個函數分解爲整個較小函數並分別置放於不一樣地點,上述步驟也就比較容易完成了。

有數個複雜精巧的模式破壞了這個規則。提及這個話題,[四巨頭]的Streategy和Visitor馬上跳入個人腦海,Kent Beck的Self Delegation也豐此列。使用這些模式是爲了對抗壞味道Divergent Change。最根本的原則是:將老是一塊兒變化的東西放在一起。[數據]和[引用這些數據]的行爲老是一塊兒變化的,但也有例外。若是例外出現,咱們就搬移那些行爲,保持[變化只在一塊兒發生]。Strategy和Visitor使你得以輕鬆修改函數行爲,由於它們將少許須要被覆寫的行爲隔離開來——固然也付出了[多一層間接性]的代價。

8. Data Clumps(數據泥團)

數據項就像小孩子:喜歡三五成羣地待在一起。你經常能夠在不少地方看到相同的三或四筆數據項:兩個classes內的相同值域、許多函數簽名式中的相同參數。這些[老是綁在一塊兒出現的數據]真應該放進屬於它們本身的對象中。首先請找出這些數據的值域形式出現點,運用Extract Class將它們提煉到一個獨立對象中。而後將注意力轉移到函數簽名式上頭,運用Introduce Parameter Object或Preserve Whole Object爲它減肥。這麼作的直接好處是能夠將不少參數列縮短,簡化函數調用動做。是的,沒必要由於Data Clumps只用上新對象的一部分值域而在乎,只要你以新對象取代兩個(或更多)值域,你就值回票價了。

一個好的評斷辦法是:刪掉衆多數據中的一筆。其它數據有沒有於是失去意義?若是它們再也不有問詢,這就是個明確信號:你應該爲它們產生一個新對象。

縮短值域個數和參數個數,固然能夠支隊一些壞味道,但更重要的是:一旦擁有新對象,你就有機會讓程序散發出一種芳香。獲得新對象後,你就能夠着手尋找Feature Envy,這能夠幫你指出[可移到新class]中的種種程序行爲。沒必要過久,全部classes都將在它們的小小社會中充分發揮本身的生產力。

9. Primitive Obsession(基本型別偏執)

大多數編程環境都有兩種數據:結構型別容許你將數據組織成有意義的形式;基本型別則是構成結構型別的積木塊。結構老是會帶來必定的額外開銷。它們有點像數據庫中的表格,或是那些得不償失的東西。

對象的一個極具價值的東西早到:它們模糊了橫亙於基本數據和體積較大的classes之間的界限。你能夠輕鬆編寫出一些與語言內置型別無異的小型classes。例如Java就以基本型別表示數值,而心class表示字符串和日期——這兩個型別在其它許多編程環境中都以基本型別表現。

對象技術的新手一般在小任務上運用小對象——像是結合數值和幣別的money class、含一個起始值和一個結束值的range class、電話號碼或郵政編碼等等的特殊strings。你能夠運用Replace Data Value with Object將本來單獨存在的數據值替換爲對象,從而走出傳統的洞窟,進入煊赫一時的對象世界。若是欲替換之數據值是type code,而它並不影響行爲,你能夠運用Replace Type Code with Class將它換掉。若是你有相依於此type code的條件式,可運用Replace Type Code with Subclass或Replace Type Code with State/Strategy加以處理。

若是你有一組應該老是被放在一塊兒的值域,可運用Extract Class。若是你在參數列中看到基本型數據,不妨試試Introduce Parameter Object。若是你發現本身正從array中挑選數據,可運用Replace Array with Object。

10. Switch Statements(switch驚悚現身)

面向對象程序的一個最明顯特徵就是:少用switch(或case)語句。從本質上說,switch語句的問題在於重複。你常會發現一樣的switch語句散佈於不一樣的地點。若是要爲它添加一個新的case子句,你必須找到全部switch語句並修改它們。面向的多態概念可爲此帶來優雅的解決辦法。

大多數時候,一看到switch語句你就應該考慮以多態來替換它。問題是多態該出如今哪兒?switch語句經常根據type code進行選擇,你要的是[與該type code相關的函數或class]。因此你應該使用Extract Method將switch語句提煉到一個獨立函數中,再以Move Method將它搬移到須要多態性的那個class裏頭。此時你必須決定是否使用Replace Type Code with Subclasses或Replace Type Code with State/Strategy。一旦這樣完成繼承結構以後,你就能夠運用Replace Conditional with Polymorphism了。

若是你只是在單一函數中髭選擇事例,而你並不想改動它們,那麼[多態]就有點殺雞用牛刀了。這種狀況下Replace Parameter with Explicit Methods是個不錯的選擇。若是你的選擇條件之一是null,能夠試試Introduce Null Object。

11. Parallel Inheritance Hierarchies(平等繼承體系)

Parallel Inheritance Hierarchies實際上是Shotgun Surgery的特殊狀況。在這種狀況下,每當你爲某個class增長一個subclass,必須也爲另外一個class相應增長一個subclass。若是你發現某個繼承體系的class名稱前綴和另外一個繼承體系的class名稱前綴徹底相同,即是聞到了這種壞味道。

消除這種重複性的通常策略是:讓一個繼承體系的實體指涉另外一個繼承體系的實體。若是再接再礪運用Move Method和Move Field,就能夠將指涉端的繼承體系消弭於無形。

12. Lazy Class(冗贅類)

你所建立的每個class,都得有人去理解它、維護它,這些工做都是要花錢的。若是一個class的所得不值其身份,它就應該消失。項目中常常會出現這樣的狀況:某個class本來對得起本身的身份,但重檐使它身形縮水,再也不作那麼多工做;或開發者事前規劃了某些變化,並添加一個class來就會這些變化,但變化實際上沒有發生。不論上述哪種緣由,請讓這個class莊嚴赴義吧。若是某些subclass沒有作知足夠工做,試試Collapse Hierarchy。對於幾乎沒用的組件,你應該以Inline Class對付它們。

13. Speculative Generality(誇誇其談將來性)

這個令咱們十分敏感的壞味道,命名者是Brian Foote。當有人說「噢,我想咱們總有一天須要作這事」並於是企圖以各式各樣的掛勾和特殊狀況來處理一些非必要的事情,這種壞味道就出現了。那麼作的結果每每形成系統更難理解和維護。若是全部裝置都會被用到,那就值得那麼作;若是用不到,就不值得。用不上的裝置只會擋你的路,因此,把它搬弄吧。

若是你的某個abstract class其實沒有太大做用,請運用Collapse Hierarchy。非必要之delegation可運用Inline Class除掉。若是函數的某些參數示被用上,可對它實施Rename Method讓它現實一些。

若是函數或class的唯一用戶是test cases,這就飄出了壞味道Speculative Generality。若是你發現這樣的函數或class,請把它們連同其test cases都刪掉。但若是它們的用途是幫助test cases檢測正當功能,固然必須刀下留人。

14. Temporary Field(使人迷惑的暫時值域)

有時你會看到這樣的對象:其內某個instance 變量僅爲某種特定情勢而設。這樣的代碼讓人不易理解,由於你一般認爲對象在全部時候都須要它的全部變量。在變量未被使用的狀況下猜想當初其設置目的,會讓你發瘋。

請使用Extract Class給這個可憐的孤首創造一個家,而後把全部和這個變量相關的代碼都放進這個新家。也許你還可使用Introduce Null Object在[變量不合法]的狀況下建立一個Null對象,從而避免寫出[條件式代碼]。

若是class中有一個複雜算法,須要好幾個變量,每每就可能致使壞味道Temporary Field的出現。因爲實現者不但願傳遞一長串參數,因此他把這些參數都放進值域中。可是這些值域只在使用該算法時纔有效,其它狀況下只會讓人迷惑。這時候你能夠利用Extract Class把這些變量和其相關函數提煉到一個獨立class中。提煉後的新對象將是一個method object。

15. Message Chains(過分耦合的消息鏈)

若是你看到用戶向一個對象索求另外一個對象,而後再向後者索求另外一個對象,而後再索求另外一個對象……這就是Message Chain。實際代碼中你看到的多是一長串getThis()或一長串臨時變量。採起這種方式,意味客戶將與查找過程當中的航行結構緊密耦合。一旦對象間的關係發生任何變化,客戶端就不得不作出相應修改。

這時候你應該使用Hide Delegate。你能夠在Message Chain的不一樣位置進行這種重構手法。理論上你能夠重構Message Chain上的任何一個對象,但這麼作每每會把全部中介對象都變成Middle Man。一般更好的選擇是:先觀察Message Chain最終獲得的對象是用來幹什麼的,看看可否以Extract Method把使用該對象的代碼提煉到一個獨立函數中,再運用Move Method把這個函數推入Message Chain。若是這條鏈上的某個對象有多位客戶打算航行此航線的剩餘部分,就加一個函數來作這件事。

有些人把任何函數鏈都視爲壞東西,咱們不這樣想。呵呵,咱們的總表明鎮定是出了名的,起碼在這件事情上是這樣。

16. Middle Man(中間轉手人)

對象的基本特徵之一就是封裝——對外部世界隱藏其內部細節。封裝每每伴隨delegation。好比說你問主管是否有時間參加一個會議,他就把這個消息委託給他的記事簿,而後才能回答你。很好,你不必知道這位主管到底使用傳統記事簿或電子記事簿抑或祕書來記錄本身的約會。

可是人們可能過分運用delegation。你也許會看到某個class接口有一半的函數都委託給其它class,這樣就是過分運用。這裏你應該使用Remove Middle Man,直接和負責對象打交道。若是這樣[不幹實事]的函數只有少數幾個,能夠運用Inline Method把它們」inlining」,放進調用端。若是這些Middle Man還有其它行爲內銷能夠運用Replace Delegation with Inheritance把它變成負責對象的subclass,這樣你既能夠擴展原對象的行爲,又沒必要負擔那麼多的委託動做。

17. Inappropriate  (狎暱關係)

有時候你會看到兩個classes過於親密,花費太多時間去探究彼此的private成分。若是這發生在兩個[人]之間,咱們沒必要作衛道之士;但對於classes,咱們但願它們嚴守清規。

就像古代戀人同樣,過份狎暱的classes必須拆散。你能夠採用Move Method和Move Field幫它們劃清界線,從而減小狎暱行徑。你也能夠看看是否運用Change Bidirectional Association to Unidirectional讓其中一個class對另外一個斬斷情絲。若是兩個classes實在情投意合,能夠運用Extract Class把二者共同點提煉到一個安全地點,讓它們坦蕩地使用這個新class。或者也能夠嘗試運用Hide Delegate讓另外一個class來爲它們傳遞相思情。

繼承每每形成過分親密,由於subclass對superclass的瞭解老是超過superclass的主觀願望。若是你以爲該讓這個孩子獨自生活了,請運用Replace Inheritance with Delegation讓它離開繼承體系。

18. Alternative Classes with Different Interfaces(殊途同歸的類)

若是兩個函數作同一件事,卻有着不一樣的簽名式,請運用Rename Method根據它們的用途從新命名。但這每每不夠,請反覆運用Move Method將某些行爲移入classes,直到二者的協議一致爲止。若是你必須重複而贅餘地移入代碼才能完成這些,或許可運用Extract Superclass爲本身贖點罪。

19. Incomplete Library Class(不完美的程序庫類)

複用常被視爲對象的終極目的。咱們認爲這實在是過分估計了。可是無能否認,許多編程技術都創建在library classes的基礎上,沒人敢說是否是咱們都把排序算法忘得一乾二淨了。

Library classes構築者沒有未卜先知的能力,咱們不能所以責怪他們。畢竟咱們本身也幾乎老是在系統快要構築完成的時候才能弄清楚它的設計,因此library構築者的任務真的很艱鉅。麻煩的是library的形式每每不夠好,每每不可能讓咱們修改其中的classes使它完成咱們但願完成的工做。這是否意味那些通過實踐檢驗的戰術如Move Method等等,現在都派不上用場了?

幸虧咱們有兩個專門就會這種狀況的工具。若是你只想修改library classes內的一兩個函數,能夠運用Introduce Foreign Method;若是想要添加一大堆額外行爲,就得運用Introduce Local Extension。

20. Data Class(幼稚的數據類)

所謂Data Class是指:它們擁有一些值域,以及用於訪問這些值域的函數,除此以外一無長物。這樣的classes只是一種[不會說話的數據容器],它們幾乎必定被其它classes過份細瑣地操控着。這些classes早期可能擁有public值域,果然如此你應該在別人注意到它們以前,馬上運用Encapsulate Field將它們封裝起來。若是這些classes內含容器類的值域,你應該檢查它們是否是獲得了恰當的封裝;若是沒有,就運用Encapsulate Collection把它們封裝起來。對於那些不應被其它classes修改的值域,請運用Remove Setting Method。

而後,找出這些[取值/設值]函數被其它classes運用的地點。嘗試以Move Method把那些調用行爲搬移到Data Class來。若是沒法搬移整個函數,就運用Extract Method產生一個可被搬移的函數。不久以後你就能夠運用Hide Method把這些[取值/設值]函數隱藏起來了。

Data Class就像小孩子。做爲一個起點很好,但若要讓它們像[成年]的對象那樣參與整個系統的工做,它們就必須承擔必定責任。

21. Refused Bequest(被拒絕的遺贈)

Subclasses應該繼承superclass的函數和數據。但若是它們不想或不須要繼承,又該怎麼辦呢?它們獲得全部禮物,卻只從中挑選幾樣來玩!

按傳統說法,這就意味繼承體系設計錯誤。你須要爲這個subclass新建一個兄弟,再運用Push Down Method和Push Down Field把全部用不到的函數下推給那兄弟。這樣一來superclass就只持有全部subclasses共享的東西。經常你會聽到這樣的建議:全部superclasses都應該是抽象的。

既然使用[傳統說法]這個略帶貶義的詞,你就能夠猜到,咱們不建議你這麼作,起碼不建議你每次都這麼作。咱們常常利用subclassing手法來複用一些行爲,並發現這能夠很好地應用於平常工做。這也是一種壞味道,咱們不否定,但氣味一般並不強烈。因此咱們說:若是Refused Bequest引發困惑和問題,請遵循傳統忠告。但沒必要認爲你每次都得那麼作。十有八九這種壞味道很淡,不值得理睬。

若是subclass複用了superclass的行爲(實現),卻又不肯意支持superclass的接口,Refused Bequest的壞味道就會變得濃烈。拒絕繼承superclass的實現,這一點咱們不介意;但若是拒絕繼承superclass的接口,咱們不覺得然。不過即便你不肯意繼承接口,也不要胡亂修改繼承系,你應該運用Replace Inheritance with Delegation來達到目的。

22. Comments(過多的註釋)

別擔憂,咱們並非說你不應寫註釋。從嗅覺上說,Comments不是一種壞味道;事實上它們仍是一種香味呢。咱們之因此要在這裏提到Comments,由於人們常把它看成除臭劑來使用。經常會有這樣的狀況:你看到一段代碼有着長長的註釋,而後發現,這些註釋之因此存在乃是由於代碼很糟糕。這種狀況的發生次數之多,實在使人吃驚。

Comments能夠帶咱們找到本章先前提到的各類壞味道。找到壞味道後,咱們首先應該以各類重構手法把壞味道去除。完成以後咱們經常會發現:註釋已經變得多餘了,由於代碼已經清楚說明了一切。

若是你須要註釋來解釋一塊代碼作了什麼,試試Extract Method;若是你須要註釋說明某些系統的需求規格,試試Introduce Assertion。

若是你不知道該作什麼,這纔是註釋的良好運用時機。除了用來記述未來的打算以外,註釋還能夠用來標記你並沒有十足把握的區域。你能夠在註釋裏寫下本身[爲何作某某事]。這類信息能夠幫助未來的修改者,尤爲是那些健忘的傢伙。

相關文章
相關標籤/搜索