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 methodeasy理解的真正關鍵在於一個好名字。假設你能給方法起個好名字,讀者就能夠通過名字了解方法的作用,根本不必去看當中寫了些什么。
終於的效果是:你應該更積極進取地分解方法。
我們遵循這樣一條原則:每當感覺須要以凝視來說明點什么的時候。我們就把須要說明的東西寫進一個獨立的方法中,並以其用途(而非實現手法)命名。
我們可以對一組或甚至短短一行代碼做這件事。哪怕替換后的方法調用動作例如法自身還長,僅僅要方法名稱可以解釋其用途。我們也該毫不猶豫地那么做。關鍵不在於方法的長度。而在於方法[做什么]和[怎樣做]之間的語義距離。
百分之九十九的場合里。要把方法變小,僅僅需使用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。這里有個實用技巧:先確定client怎樣使用它們,然后運用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(發散式變化)
我們希望軟件可以更easy被改動——畢竟軟件再怎么說本來就該是[軟]的。
一旦須要改動,我們希望可以跌到系統的某一點,僅僅在該處做改動。假設不能做到這點,你就嗅出兩種緊密相關的刺鼻味道中的一種了。
假設某個class常常由於不同的原因在不同的方向上發生變化,Divergent Change就出現了。
當你看着一個class說:“ 呃,假設新增加一個數據庫。我必須改動這三個方法;假設新出現一種金融工具,我必須改動這四個方法”,那么此時或許將這個對象分成兩個會更好,這么一來每一個對象就能夠僅僅因一種變化而須要改動。
當然,往往僅僅有在增加新數據庫或新金融工具后,你才干發現這一點。針對某一外界變化的全部對應改動。都僅僅應該發生在單一class中,而這個新class內的全部內容都應該反應該外界變化。為此,你應該找出因着某特定原因而造成的全部變化,然后運用Extract Class將它們提煉到還有一個class中。
6.Shotgun Surgery(霰彈式改動)
Shotgun Surgery類似Divergent Change。但恰恰相反。假設每遇到某種變化,你都必須在很多不同的class內做出很多小改動以響應之。你所面臨的壞味道就是Shotgun Surgery。假設須要改動的代碼散布四處。你不但非常難找到它們。也非常easy忘記某個重要的改動。
這樣的情況下你應該使用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將這種方法分解為整個較小方法並分別置放於不同地點,上述步驟也就比較easy完畢了。
有數個復雜靜止的模式破壞了這個規則。
說起這個話題,[四巨頭]的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()或一長串暫時變量。採取這樣的方式,意味客戶將與查找過程中的航行結構緊密耦合。
一旦對象間的關系發生不論什么變化,client就不得不做出對應改動。
這時候你應該使用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(純稚的數據類)
所謂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。
假設你不知道該做什么,這才是凝視的良好運用時機。
除了用來記述將來的打算之外。凝視還能夠用來標記你並無十足把握的區域。你能夠在凝視里寫下自己[為什么做某某事]。
這類信息能夠幫助將來的改動者,尤其是那些健忘的家伙。