重構之22種代碼的壞味道

概述:

在編寫代碼時,隨着代碼量的增加,會時不時的需要重構來提高代碼的質量。以下是22中代碼的壞味道,如果你的代碼中也有這些,那快快來按照這些方法重構你的代碼吧!

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將其它方法的算法替換掉。
        如果兩個毫不相關的classes內出現Duplicated Code,你應該考慮對其中一個使用Extract Class,將重複代碼提煉到一個獨立class中,然後在另一個class內使用這個新class。但是,重複代碼所在的方法也可能的確只應該屬於某個 class,另一個class只能調用它,抑或這個方法可能屬於第三個class,而另兩個classes應該引用這第三個class。你必須決定這個方法放在哪兒最合適,並確保它被安置後就不會再在其它任何地方出現。

2.Long Method(過長方法)  

--->難理解   [解決方法]:拆分成若干函數
       擁有[短方法](short methods)的對象會活得比較好、比較長。不熟悉面向對象技術的人,常常覺得對象程序中只有無窮無盡的delegation(委託),根本沒有進行任何計算。和此類程序共同生活數年之後,你纔會知道,這些小小方法有多大價值。[間接層]所能帶來的全部利益——解釋能力、共享能力、選擇能力——都是由小型方法支持的。
        很久以前程序員就已認識到:程序愈長愈難理解。早期的編程語言中,[子程序調用動作]需要額外開銷,這使得做你們不太樂意使用small method,現代OO語言幾乎已經完全免除了進程內的[方法調用動作額外開銷]。不過代碼閱讀者還是得多費力氣,因爲他必須經常轉換上下文去看看子程序做了什麼。某些開發環境允許用戶同時看到兩個方法,這可以幫助你省去部分麻煩,但是讓small method容易理解的真正關鍵在於一個好名字。如果你能給方法起個好名字,讀者就可以通過名字瞭解方法的作用,根本不必去看其中寫了些什麼。
        最終的效果是:你應該更積極進取地分解方法。我們遵循這樣一條原則:每當感覺需要以註釋來說明點什麼的時候,我們就把需要說明的東西寫進一個獨立的方法中,並以其用途(而非實現手法)命名。我們可以對一組或甚至短短一行代碼做這件事。哪怕替換後的方法調用動作比方法自身還長,只要方法名稱能夠解釋其用途,我們也該毫不猶豫地那麼做。關鍵不在於方法的長度,而在於方法[做什麼]和[如何做]之間的語義距離。
        百分之九十九的場合裏,要把方法變小,只需使用Extract Method。找到方法中適合集在一起的部分,將它們提煉出來形成一個新方法。
        如果方法內有大量的參數和臨時變量,它們會對你的方法提煉形成阻礙。如果你嘗試運用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(基本型別偏執)  

--->熱衷於使用int,long,String等基本類型   [解決方法]:反覆出現的一組參數,有關聯的多個數組換成類吧
        大多數編程環境都有兩種數據:結構型別允許你將數據組織成有意義的形式;基本型別則是構成結構型別的積木塊。結構總是會帶來一定的額外開銷。它們有點像數據庫中的表格,或是那些得不償失的東西。
        對象的一個極具價值的東西早到:它們模糊了橫亙於基本數據和體積較大的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驚悚現身)  

--->  [解決方法]:state/strategy 或者只是簡單的多態
        面向對象程序的一個最明顯特徵就是:少用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(平等繼承體系)  

--->增加A類的子類ax,B類也需要相應的增加一個bx   [解決方法]:應該有一個類是可以去掉繼承關係的
        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 Intimacy(狎暱關係)  

--->兩個類彼此使用對方的私有的東西  [解決方法]:劃清界限拆散,或合併,或改成單項聯繫
        有時候你會看到兩個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(純稚的數據類)  

--->類很簡單,僅有公共成員變量,或簡單操作函數   [解決方法]:將相關操作封裝進去,減少public成員變量
        所謂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。
        如果你不知道該做什麼,這纔是註釋的良好運用時機。除了用來記述將來的打算之外,註釋還可以用來標記你並無十足把握的區域。你可以在註釋裏寫下自己[爲什麼做某某事]。這類信息可以幫助將來的修改者,尤其是那些健忘的傢伙。


以上內容出自以下兩片博客:
http://blog.csdn.net/sulliy/article/details/6635596
http://blog.csdn.net/windcao/article/details/25773219

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章