【抽象那些事】不完整的抽象&多方面抽象&未用的抽象&重復的抽象
不完整的抽象
抽象未支持所有互補或相關的方法時,將導致這種壞味。
為什麽要有完整的抽象?
一種重要的抽象實現手法是創建內聚而完整的抽象。抽象未支持相關的方法時,可能會影響抽象的內聚性和完整性。如果抽象只支持部分相關的方法,其使用者就可能不得不自己去實現其他的功能。客戶程序可能嘗試直接訪問抽象的內部實現細節,此時帶來的副作用是違反封裝原則。
一些常見的互補方法對
Min/Max | Open/Close | Create/Destroy | Get/Set |
---|---|---|---|
Read/Write | Print/Scan | First/Last | Begin/End |
Start/Stop | Lock/Unlock | Show/Hide | Up/Down |
Source/Target | Insert/Delete | First/Last | Push/Pull |
Enable/Disable | Acquire/Release | Left/Right | On/Off |
現實考慮
禁止特定行為
設計人員可能有意識地做出不提供堆成或配套方法的設計決策。例如在只讀集合中,包含Add()方法,而不包含Remove()方法。
使用單個方法而不是成對的方法
例如用SetEnabled(bool)替換Enable()和Disable(),傳入true代表啟用,fasle代表禁用。
多方面抽象
抽象被賦予不止一項職責時,將導致這種壞味。
為什麽不可以有多方面抽象?
單一職責原則指出,抽象必須承擔單一而明確的職責,且必須完全封裝該職責。抽象承擔了多種職責時,意味著它將受多種原因的影響而需要修改,設計的修改頻率與其缺陷數之間存在很強的正相關關系。這意味著多方面抽象存在的缺陷可能更多。
多方面抽象的潛在原因
通用抽象
引入使用通用名(如Item,Order,Product,Image)的抽象時,它常常會成為占位符,用於提供所有相關(但未必屬於它)的功能。抽象的命名代表了這個抽象的職責,命名太通用,隨著系統的叠代,抽象會慢慢承擔多種職責。感同身受!!!
未定期重構
對類進行了大量修改而沒有定期重構,長此以往,可能就會在類中引入了額外的職責。
混合關註點
沒有對關註點分離給予足夠的重視。
重構建議
類承擔了多種職責時,就不是內聚的。可以使用“提取類”來進行重構。
未用的抽象
創建的抽象未用(未被直接使用或繼承)時,將導致這種壞味。有以下兩種表現形式:
- 未引用的抽象:未用的具體類
- 鰥寡抽象:沒有任何派生抽象的接口/抽象類
為什麽不可以有未用的抽象?
設計中的抽象未被使用,就沒有發揮任何作用,因此違反了抽象原則。未實現的抽象類和接口時多余的或憑空想象出來的概括,因此是不需要的。
未用的抽象潛在原因
憑空想象的設計
試圖設計"永不過時"的系統或在其中包含"未來可能用得著"的抽象時,將導致這種壞味。
不斷變化的需求
需求不斷變化,為滿足早期需求而創建的抽象可能已經不再需要。如果將其留在設計中,它將變成未用的抽象。
維護過程中留下的垃圾
維護或重構時,如果不清理舊的抽象,可能留下未引用的抽象。這點深有體會,所以一直要求組員在重構的過程中,一定要把舊代碼刪除。如果不這樣做,過期的和未用的代碼將導致代碼庫急劇膨脹,重構的代碼和未重構的代碼糾纏在一起,代碼的可理解性、閱讀體驗極差。而且如果你重構的舊代碼你不負責刪除,其他人就更不知道如何下手了,久而久之這些舊代碼就會變成BUG的溫床。註釋掉舊代碼也不是一個好的選擇,太影響閱讀體驗。其次,現在的代碼版本控制工具功能強大,即使刪除錯了代碼,也可以通過版本控制工具找回。所以舊代碼必須死。
擔心破壞既有代碼
不確定是否還有其他代碼在使用想要刪除的舊代碼。
重構建議
將未用的抽象從設計中刪除。對於可能還有客戶程序在使用的API,直接刪除不可行,可將這些抽象標記為‘‘過期的‘‘或"已摒棄",明確地指出在新開發的客戶程序中不得使用它們。
[Obsolete]
public class Report
{
}
現實考慮
類庫和框架通常以抽象類或接口的方式提供擴展點,這些抽象類可能在庫或框架中未被使用,但它們是供客戶程序使用的擴展點,因此不屬於未用的抽象。
重復的抽象
兩個抽象的名稱、實現或兩者相同時,將導致這種壞味。
- 名稱相同
兩個不同的抽象重名將影響可理解性。
- 實現相同
多個抽象的成員定義在語義上相同,但在設計上沒有捕獲並使用這些實現中相同的元素。在繼承層析結構中,如果多個兄弟抽象的實現相同,可能意味著存在的是"未歸並的層次結構"壞味。
- 名稱實現都相同
為什麽不可以有重復的抽象?
重復代碼是軟件萬惡之首。所以我們要極力避免重復。
如果多個抽象的名稱相同,將影響設計的可理解性:客戶代碼開發人員將不知道使用哪個抽象。
如果多個抽象的實現相同(代碼相同),將難以維護:修改其中一個抽象的實現時,常常需要修改其它所有重復抽象的實現。這不僅增加了修改負擔,還可能引入難以發現的微小BUG。為縮小修改範圍,必須盡可能避免重復。
重復的抽象潛在原因
復制粘貼編程手法
CV程序員復制並粘貼代碼,而不應用合適的抽象。
即興維護
經過多年的修復或改進後,軟件將包含"殘留",其中有大量重復的代碼。
交流不暢
不同時期通常由不同的人員負責維護軟件,他們對軟件的了解不徹底,編寫了原來就有的類或方法,導致軟件包含重復的代碼。
類被聲明為不可擴展的
類被聲明為不可擴展的,無法重用代碼,只能復制代碼,創建修訂版本。
重構建議
對於名稱相同的重復抽象,可以將其中一個抽象改為不同的名稱。
對於實現相同的重復抽象,如果實現完全相同,可將其中一個抽象刪除。如果實現稍有差異,可將相同的實現歸並到另一個類中:這可以是層次結構中的基類,也可以是重復的抽象可引用或使用的既有類或新類。
現實考慮
適應變化
導致重復抽象的一個原因是,要同時支持同步和非同步變種。
在不同的上下文中使用相同的類型名
對於大型系統,建立完全統一的領域模型要麽不可行要麽不劃算。領域驅動設計提供的一種解決方案是,將大型系統分成多個"界限上下文"。采用這種方式,不同上下文中的模型可能包含同名的類型,但是這是可以接受的。
語言未提供重復避免支持
在JDK中,有很多的重復的方法和類,這是因為沒有對基本類型提供泛型支持。但是在.Net中就不會有這麽多重復的方法和類,因為C#對基本類型提供了泛型支持。
參考:《軟件設計重構》
作者:喜歡天黑卻怕鬼
來源:http://songwenjie.cnblogs.com/
聲明:本文為博主學習感悟總結,水平有限,如果不當,歡迎指正。如果您認為還不錯,不妨點擊一下下方的【推薦】按鈕,謝謝支持。轉載與引用請註明出處。
【抽象那些事】不完整的抽象&多方面抽象&未用的抽象&重復的抽象