設計模式五大基本原則 SOLID

最近複習 Design Pattern 順手做個筆記,Design Pattern 的是基本 5 大原則 SOLID。先說明這部分的名詞翻譯,中文翻譯我是查詢 維基百科 的翻譯,畢竟中文翻譯方式本身就是一個約定成俗即可的東西。

縮寫英文全名中譯
SRPSingle Responsibility Principle單一職責原則
OCPOpen Closed Principle開閉原則
LSPLiskov Substitution Principle里氏替換原則
ISPInterface Segregation Principles介面隔離原則
DIPDependency Inversion Principle依賴反轉原則

讓我們針對這幾個逐一做介紹:

單一職責原則 Single Responsibility Principle

從最高維的系統角度來看,每個系統該明確規劃並解決某中問題,或是針對某個功能的實作,而不是一個功能複雜卻難用的系統,可以想像成一個 APP 就是做一件事情,日曆就是日曆,過多的功能只會導致開發上的複雜或是需求定位不夠明確的問題。若是從低維度往上看,程式碼中的 Function 應該遵循所謂的 Pure Function,每個函數負責好自己做的事情,一次只做一件事情,不依賴於外界任何的東西,也不多做任何其他的手腳。

單一職責原則最終的目的是 高內聚。一次做一件事情,並移除與這件事情無關的程式碼和變數,整體程式碼中的每個部分都與自己實作的功能相關,並沒有冗余的程式碼或功能。可以將單一職責原則視為樂高的每一塊積木,積木就做好一塊正方型積木堆疊的工作,草形狀的積木就是裝飾草形狀的工作,要是有一塊積木是正方形的同時上面又長草,或許在某些情境下特別好用,但這也代表他在其他地方不適用,因為他組合了兩個不同的功能,並非單一職責原則。

開閉原則 Open Closed Principle

一個模組應該是透過延展功能的彈性來獲得更多功能上的變化,而並非直接修改內部的程式碼。將模組設計的易於延展功能,實務上的困難就是難以預測,到底模組中的哪些功能有可能會面對到修改。目的是避免最原始的模組被頻繁的修改,基層模組被頻繁修改時相當難維護其穩定性,OCP 希望直接避免掉修改的機會,使用延展新功能的方式來達成不同的需求。而新的需求就從新延展出來的模組中來實作,這樣維持舊有程式碼的安定性和新需求的彈性。

明顯的,延展模組就是說物件導向中的繼承,而針對延展方式也有不同的手法,如直接繼承就有的模組並覆蓋新功能上去、繼承抽象類別、透過抽象類別的規範來實作新功能、透過多行來實作不同的新功能。

里氏替換原則 Liskov Substitution Principle

在繼承中衍生出來的子類別,能完全支援父類別的功能。之所以稱做替換原則,就是指腹類別成出現的地方,一定能替換成衍生類別來使用。這中間其實多少有牽扯到 SRP 的原則,當你的模組或衍生的模組夾雜太多功能時,儘管依照著 LSP 的原則元整的實作了上一階的所有功能是相當不錯的方式,但這樣會造成這個類別或是模組越來越複雜,而當面臨下一次的功能擴充時,這個狀況會越來越明顯的陷入泥濘。因此在嘗試 LSP 時,必定要注意到不能忽略了 SRP 的觀念,避免在多次的升級或是改版中,最終導致整個系統極低的內聚力,並且相當不易拆分或刪減功能維護。

介面隔離原則 Interface Segregation Principles

設計模組時避免設計龐大且同時多功能的單一介面,我們不應該大規模的假設使用者會完全符合這樣的需求。介面設計時應該力求介面的功能單一化,分割出多個單一功能的碎片,而眾多的功能介面中,使用者只需要挑出幾個符合需求的,並了解之後拼裝成對應的功能。同樣與 SRP 有類似的觀念,當某個 API 設計越複雜越完整時,這只會導致該 API 能適用的場合越來越少,多做了不該做的事情。ISP 即是esign Pattern 中的 低耦合 目標。

依賴反轉原則 Dependency Inversion Principle

重點在於誰依賴誰,是設計模式中相當重要的事情,子類別肯定依賴父類別的,而父類別不能去依賴子類別實作功能。另一個重點是抽象類別,抽象類別不應該有太多細節,而實作類別依賴抽象細節,這之間的重點是,依賴應該是單向的,當然最好的目標是完全的沒有耦合,但繼承這個行為本身就讓某個類別依賴另一個類別,因此單向的依賴是最乾淨的,雙向的依賴,或是違反常理的依賴,會導致程式碼相當難追蹤。

結語

五個基本原則大致上勾勒出:低耦合高內聚、介面設計方式、繼承方式、兼容。而實作設計模式的方式與細節重點在於讓程式碼更乾淨,更能讓後者完全了解前者所撰寫的功能,也降低在後續修改上的成本。

分享到