時隔一個月,好不容易有時間回頭來看看這本書了,在這篇筆記中我把第二章節切兩個小節,分別為:設計臭味與如何解決設計臭味。
前面一章節的筆記中討論了一些造成技術債的種種原因,其中一種原因就是本章節的重點:設計臭味。在這本書中,作者以列舉數種設計臭味的方式,並透過 例子🌰 討論如何解決它們來幫助我們可以更好認識軟體設計的原則。
首先是以 java.uil 套件中的 Calendar 類別為例子。這個類別實現了現實世界中的日曆,在預期中它應該只會支援到日期相關的功能,但實際上他還支援了時間相關的功能。
一個抽象應該只能承擔一中職責
這個類別承擔了多個職責已經違反了抽象原則(單一職責原則),通常這種臭味稱之為多方面抽象臭味,因為其承擔了多個職責。
以 java.util.Stack類別為例,他擴充了java.util.Vector.Stack。 但Stack與Vector之間並不存在 is-a 的關係。
ChatGpt: "is-a" 關係是物件導向程式設計(OOP)中的一個重要概念,用於描述類別(Class)之間的繼承關係。這種關係表示一個類別(子類別或衍生類別)是另一個類別(父類別或基底類別)的特化。換句話說,子類別具有父類別的所有特性,同時可能擁有自己的特性或行為。
因此,這種設計模式違反了hierachy原則(可以說是替換原則,LSP)。書中將這種臭味稱為:支離破碎行層次結構,因為它破壞了可替換性。
開發人員通常會使用些著名的設計模型作為解決方案來解決問題,但時常發生開發人員急於應用這些模型,而沒有充分理解到必須平衡各種作用力。造成的結果就會有過多的類別亦或是類別之間的高度耦合。
要應用設計模型,必須有條不紊並考慮周全
單一種設計模型可能存在數百個變形,而每個變形的影響都不大相同,如果對於變形的各個面向和影響沒有全盤的認識,使用他反而會嚴重影響設計品質。
設計模式與設計臭味存在著一種關係。要消除臭味,通常都是使用設計模型進行消除。然而錯誤的使用反而會導致設計臭味,對此書上稱之為反模式
使用的程式語言的不足也可能會導致設計臭味。
書中以Java為例,最初的Java並不支援列舉(Enum),開發者不得不用類別或是介面來存放常數,而這在設計中就會引入臭味:不必要的抽象(Unnecessary Abstraction)
另一個例子,在JDK中,AbstractQueuedSynchronizer 和 AbstractQueuedLongSynchronizer 類別都是直接從 AbstactOwnableSynchronizer 衍生而來,但其所支援的基本型別卻是int和long。
這就導致了設計臭味:未合併的層次結構(Unfactored Hierarchy)。但由於當時的Java的泛型功能不支援基本型別,也無法消除這樣的程式重複。
這點偏向於開發者的開發心態,假設今天的開發者是從程序導向轉至物件導向的專案時,常常會誤以為類別是提供功能,而不是表示事物。進行編寫時就可能會在命名的時候將類別名字取為動詞、進行功能分解、不利用多型而進行顯示型別檢查等,造成設計臭味。
開發人員可能會為了滿足特定需求,不採用系統式的手法(書中未寫到,但這邊我自認為是拉設計模型此類的),而是求助於權宜之計,進而導致導致設計臭味。
導致開發人員這樣做的原因之一就是黏滯性 (viscosity)。
指的是採用正確的方案解決問題時必定遇到的 阻力(即完成所花費的功)。但可能礙於時間限制問題,開發人員會退而求其次的選擇功耗較少的權宜之計,進而導致設計臭味。
指的是遵循最佳實踐時,必須克服的開發環境帶來的阻力,例如人力不足。
通常如果,開發環境人力不足導致開發緩慢,導致遵循最佳實踐需要功耗比採取糟糕做法來得多,開發人員將採取糟糕的做法。
這裡對於code層面影響較低,透過簡單的話來說:軟體開發是項極大的工程,會需要眾多的人力介入。因此遵循最佳實踐與程序流程,在工程中是件重要的事情。 (即須遵守Guideline)