如果是尿布抽了咆蒿,就換掉它癞蚕。 —— Beck奶奶
3.1 Duplicated Code
最單純的Duplicated Code是同一個類的兩個函數(shù)含有相同的表達(dá)式妈拌;另一種常見的情況就是兩個互為兄弟的子類內(nèi)含相同的表達(dá)式迟杂,構(gòu)筑超類或者使用模板方法;如果兩個毫不相關(guān)的類出現(xiàn)Dulicated Code缀蹄,提取獨立類峭跳。
3.2 Long Method
擁有短函數(shù)的對象會獲得比較好、比較長袍患。間接層所能帶來的全部利益——解釋能力坦康、共享能力竣付、選擇能力——都是由小型函數(shù)支持的诡延。
3.3 Large Class
如果單個類想做太多的事情,期內(nèi)往往會有太多的實例變量古胆。這往往意味著Duplicated Code肆良。
3.4 Long Parameter List
太長的參數(shù)無法理解,太多的參數(shù)同時也會容易造成前后不一致逸绎。
3.5 Divergent Change(發(fā)散式修改)
我們希望軟件能夠更容易被修改惹恃。如果某個類經(jīng)常因為不同的原因在不同的方向上發(fā)生變化。最好能將類切開棺牧,這也是遵循單一職責(zé)原則巫糙。
3.6 Shotgun Surgery(散彈式修改)
Shotgun Surgery和Divergent Change類似,但是恰恰相反颊乘。如果每遇到某種變化参淹,你都必須在許多不同類內(nèi)做出許多小修改。
總之乏悄,Divergent Change是指一個類受多種變化影響浙值。Shortgun Surgery則是指一個變化引起多個類相應(yīng)修改。
3.7 Feature Envy(依戀情節(jié))
對象技術(shù)的全部要點在于:這是一種“將數(shù)據(jù)和對數(shù)據(jù)的操作行為包裝在一起”的技術(shù)檩小。有一種經(jīng)典的氣味:函數(shù)對某個類的興趣搞過對自己所處類的興趣开呐。例如:某個類為了計算某個值,從某個類中獲取幾乎半打的取值函數(shù)规求。這種函數(shù)筐付,應(yīng)該將這個函數(shù)移動到它應(yīng)該在的地方。
判斷類的地址的原則:判斷哪個類擁有最多被此函數(shù)使用的數(shù)據(jù)阻肿,然后該函數(shù)就乖乖去了那里瓦戚。Strategy和Visitor就是對抗Divergent Change。最根本的原則:將總是一起變化的東西放在一起冕茅,保持變化永遠(yuǎn)只在一處發(fā)生伤极。當(dāng)然蛹找,你不得不多加一層抽象層次。
3.8 Data Clumps
數(shù)據(jù)項就像小孩子哨坪,喜歡成群結(jié)隊地待在一塊兒庸疾。常常可以在很多地方看到相同的三四項數(shù)據(jù):兩個類中的相同字段当编、許多函數(shù)簽名中的參數(shù)届慈。這些總是綁定在一起出現(xiàn)的數(shù)據(jù)真應(yīng)該擁有屬于它們自己的對象。
一個好的評判方法:刪除眾多數(shù)據(jù)中的一項忿偷。這么做金顿,其他數(shù)據(jù)沒有失去意義?如果不再有意義鲤桥,這就是明確的意義揍拆,告訴你應(yīng)該為他們產(chǎn)生一個新對象。
3.9 Primitive Obsession(基本類型偏執(zhí))
大多數(shù)編程環(huán)境都有兩種數(shù)據(jù):結(jié)構(gòu)類型允許你將數(shù)據(jù)組織成有意義的形式茶凳;基本類型則是構(gòu)筑結(jié)構(gòu)類型的積木塊嫂拴。結(jié)構(gòu)總是會帶來一定的額外開銷。他們可能代表著數(shù)據(jù)中的表贮喧,如果僅僅為了做一兩件事而穿件結(jié)構(gòu)類型也可能顯得太麻煩了筒狠。
3.10 Switch Statement(驚悚的switch)
面向?qū)ο蟪绦虻囊粋€最棉線特征就是:少用switch(或case)語句。
大多數(shù)的時候箱沦,一看到switch語句辩恼,你就應(yīng)該考慮以多態(tài)來替代它;但是在單一函數(shù)中有些選擇事例谓形,且并不想改動它們灶伊,那么多態(tài)就有點殺雞用牛刀了。
3.11 Parallel Inheritance Hierarchies(平行繼承體系)
其實是Shotgun Surgery的特殊情況套耕。即一個類增加一個子類谁帕,另一個類也必須增加一個子類。
一般則略:讓一個繼承體系的實例引用另一個體系的實例冯袍。
3.12 Lazy Class(冗余類)
如果一個類不值其身價匈挖,它就應(yīng)該消失。
3.13 Speculative Generality(夸夸其談未來性)
因為主觀臆測的未來性(未來某天我們需要做這件事情)康愤,并其余以各種各樣的鉤子和特殊情況來處理一些非必要的事情儡循。
注意:當(dāng)函數(shù)或者類的唯一用戶是測試用例,壞味道就飄出來了征冷。
3.14 Temporary Field(令人迷惑的臨時字段)
對象內(nèi)某個實例變量僅為某特定情況而設(shè)择膝。這樣的代碼會讓讓人無法理解,通常認(rèn)為對象在所有時候都需要它所有變量检激。猜測變量的用途會讓人crazy肴捉。
3.15 Message Chain(過度偶爾得消息鏈)
如果你可以看到用戶向一個對象請求另一個對象腹侣,然后再向后者請求另一個對象,然后再請求另一個對象……這就是消息鏈齿穗。采取這種方式傲隶,意味著用戶代碼將與查找過程中的導(dǎo)航結(jié)構(gòu)緊密耦合。一旦對象間的關(guān)系發(fā)生任何變化窃页,客戶端也的變化跺株。
可以使用消息隊列模式來進(jìn)行重構(gòu)。
3.16 Middle Man
對象的基本特征之一就是封裝——對外部空間隱藏起內(nèi)部實現(xiàn)細(xì)節(jié)脖卖。封裝也代表著委托乒省。但是也有時候會成為過度的委托。我們會看到某個類接口將其一半的實現(xiàn)委托給其他類畦木。
3.17 Inappropriate Intimacy(狎昵關(guān)系)
有時你會看到兩個類過于親密袖扛,花費太多時間探究彼此的private成分。過分狎昵必須分開馋劈。集成往往造成過度親密攻锰,因為子類對超類的了解總是超過后者的主觀愿望晾嘶。需要的時候妓雾,讓類離開類繼承關(guān)系鏈。
3.18 Alternative Classes with Diffierent Interaces(異曲同工的類)
如果兩個函數(shù)做同一件事情垒迂,卻有著不同的簽名械姻,請根據(jù)用于合并和修改。
3.19 Incomplete Library Class(不完美的庫類)
復(fù)用被視為對象的終極目的机断。不過楷拳,往往復(fù)用的意義經(jīng)常被高估——大多數(shù)對象只要夠用就好。有時候需要根據(jù)需要修改類庫吏奸。
3.20 Data Class(純稚的數(shù)據(jù)類)
Data Class是指它們擁有一些字段欢揖,以及用于訪問(讀寫)這些字段的函數(shù),除此之外一無長處奋蔚。這種類往往被其他類過分操縱她混。這些類可能擁有public字段,果真如此泊碑,需要在別人注意到之前封裝起來坤按;如果這些類內(nèi)含容器類的字段,你應(yīng)該前叉它們是否得到了恰當(dāng)?shù)姆庋b馒过。如果沒有則正確封裝它們臭脓。然后找出取值/設(shè)值函數(shù)被其他類運用的地點。嘗試以Move Method將調(diào)用行為移動到Data Class中腹忽。
3.21 Refused Bequest(被拒絕的遺贈)
子類應(yīng)該繼承超類的函數(shù)和數(shù)據(jù)来累。但是如果不想繼承所有砚作,怎么選擇其中一部分。這就意味著繼承體系設(shè)計錯誤嘹锁。給子類設(shè)計兄弟類偎巢,下移動一部分字段到兄弟類中。從而正確構(gòu)建體系兼耀。
3.22 Comments(過多的注釋)
Comments本身是帶著香味压昼,然而不恰當(dāng)?shù)淖⑨屖窍喈?dāng)臭氣味。只有當(dāng)不知道如何重構(gòu)的時候瘤运,使用注釋才是優(yōu)質(zhì)的方式窍霞。