[設計模式] 軟體設計模式的 SOLID 原則與相關設計原則整理

本文是對軟體設計模式的基本原則的整理與個人心得

前言

設計模式 Design Pattern 對很多軟體設計工作者來說不是很熟悉簡直是陌生!

記得曾有指導的工程師問我為什麼接手的軟體功能怎麼不平鋪直敘一本道到底,幹嘛要依照命令的格式寫功能函式 (當時是用 MVVM )?

當時我是跟他說這是要協同合作~

尤其當時看到幾乎每個接手歷史程式碼的人都要花蠻久時間理解,最終幾乎又是重新改過內容,這情況真的不妙!

所以說採用設計模式來執行軟體設計就是要跟其他人協同合作,跟以前/未來的自己協同合作,更進一步是協同合作整個系統

當開發的系統越來越龐大複雜,加入開發的軟體工程師越來越多,要維持的軟體品質就離不開依照合適的設計模式來撰寫程式碼

設計模式可以看做軟體設計的心法,讓開發程式可以更容易理解、更多的彈性復用!

這篇就整理一下設計模式的核心精神 - SOLID 原則,可以說各種設計模式的出現就是為了符合這核心原則

SOLID原則

SOLID 原則由 21 世紀初 Robert C. Martin 整合五個物件導向設計原則而提出的方便記憶的首字母縮略字,這五個是由不同時空背景跟人各別提出的喔!

以下就彙整一下這五個原則的內容描述與要點提示

單一職責原則 (Single Responsibility Principle, SRP)

認為「對象應該僅具有一種單一功能」的概念。

單一功能原則 (Single responsibility principle) 規定每個類都應該有一個單一的功能,並且該功能應該由這個類完全封裝起來。所有它的 (這個類的) 服務都應該嚴密的和該功能平行 (功能平行,意味著沒有依賴)。

筆記:封裝、一個類只完成一個需求的功能 (這一部分有些彈性,由於每個人對需求的定義範圍不同)

開放封閉原則 (Open/Closed Principle, OCP)

認為「軟體應該是對於擴充開放的,但是對於修改封閉的」的概念。

開閉原則 (Open/Closed Principle) 規定「軟體中的對象 (類,模塊,函數等等) 應該對於擴展是開放的,但是對於修改是封閉的」,這意味著一個實體是允許在不改變它的原始碼的前提下變更它的行為。該特性在產品化的環境中是特別有價值的,在這種環境中,改變原始碼需要代碼審查,單元測試以及諸如此類的用以確保產品使用品質的過程。遵循這種原則的代碼在擴展時並不發生改變,因此無需上述的過程。

一般被認為 Bertrand Meyer (Eiffel程式語言的創造者,提出契約式設計觀念)是最早提出開閉原則這一術語的人。認為一旦完成一個類的實現只應該因錯誤而修改,新的或者改變的特性應該通過新建不同的類實現。新建的類可以通過繼承的方式來重用原類的代碼。衍生的子類可以或不可以擁有和原類相同的接口。

筆記:一個類對修改既有功能封閉,對新增功能開放

里氏替換原則 (Liskov Substitution Principle, LSP)

認為「程式中的對象應該是可以在不改變程式正確性的前提下被它的子類所替換的」的概念 (參考契約式設計)。
里氏替換原則 (Liskov Substitution principle) 是對子類型的特別定義。由 Barbara Liskov 在 1987 年在一次會議上名為「資料的抽象與層次」的演說中首先提出

Robert C. Martin 對此的定義為「衍生類別 (子類) 對象可以在程式中代替其基礎類別 (超類) 對象。」

筆記:子類別可以獨立達成父類別所有功能

介面隔離原則 (Interface Segregation Principle, ISP)

認為「多個特定客戶端介面要好於一個寬泛用途的介面」的概念。 

介面隔離原則 (Interface Segregation Principles) 指明客戶 (Client) 不應被迫使用對其而言無用的方法或功能。介面隔離原則拆分非常龐大臃腫的介面成為更小的和更具體的介面,這樣客戶將會只需要知道他們感興趣的方法。這種縮小的介面也被稱為角色介面 (Role Interfaces)。介面隔離原則的目的是系統解開耦合,從而容易重構,更改和重新部署。

筆記:具體類只依賴在提供需求功能的最小介面上

依賴反轉原則 (Dependency Inversion Principle, DIP)

認為一個方法應該遵從「依賴於抽象而不是一個實例」的概念。依賴注入是該原則的一種實現方式。

依賴反轉原則 (Dependency inversion principle) 是指一種特定的解耦 (傳統的依賴關係建立在高層次上,而具體的策略設定則應用在低層次的模組上) 形式,使得高層次的模組不依賴於低層次的模組的實現細節,依賴關係被顛倒 (反轉),從而使得低層次模組依賴於高層次模組的需求抽象。

該原則規定:

  1. 高層次的模組不應該依賴於低層次的模組,兩者都應該依賴於抽象介面。
  2. 抽象介面不應該依賴於具體實現。而具體實現則應該依賴於抽象介面。

筆記:不同層次的具體類應該透過抽象介面做縱向關聯

總結

SOLID 原則最主要的目標是達成程式的高內聚,低耦合

  • 內聚性 (Cohesion) 也稱為內聚力,是一軟體度量,是指機能相關的程式組合成一模組的程度,或是各機能凝聚的狀態或程度。
  • 耦合性 (Coupling) 或稱耦合力或耦合度,是一種軟體度量,是指一程式中,模組及模組之間資訊或參數依賴的程度。

其他原則

除了 SOLID 原則外,也有其他原則(或是定律)被提出遵循,但都可以在 SOLID 原則中找到對應關係;以下補充幾個常見原則/定律

得墨忒耳定律 (Law of Demeter, LoD)

左邊鄰居稱為迪米特法則,亦被稱作「最少知識原則 (Principle of Least Knowledge)」,是一種軟體開發的設計指導原則,特別是物件導向的程序設計。得墨忒耳定律是鬆耦合的一種具體案例。

簡單地以下面任一種方式總結:

  • 每個單元對於其他的單元只能擁有有限的知識:只是與當前單元緊密聯繫的單元
  • 每個單元只能和它的朋友交談:不能和陌生單元交談
  • 只和自己直接的朋友交談

得墨忒耳定律使得軟體更好的可維護性與適應性。因為對象較少依賴其它對象的內部結構,可以改變對象容器(container)而不用改變它的調用者(caller)。

KISS 原則 (Keep It Simple, Stupid)

由洛克希德公司 (Lockheed Corporation) 的首席工程師 Clarence Leonard "Kelly" Johnson 所創造的。KISS 原則是指在設計當中應當注重簡約的原則。總結工程專業人員在設計過程中的經驗,大多數系統的設計應保持簡潔和單純,而不摻入非必要的複雜性,這樣的系統運作成效會取得最優;因此簡單性應該是設計中的關鍵目標,儘量避免不必要的複雜性。

契約式設計 (Design by Contract, DbC)

這個術語最早由伯 Bertrand Meyer 於1986年提出。一種設計電腦軟體的方法。這種方法要求軟體設計者為軟體組件定義正式的,精確的並且可驗證的介面,這樣,為傳統的抽象資料類型又增加了先驗條件、後驗條件和不變式。這種方法的名字裡用到的「契約」或者說「契約」是一種比喻,因為它和商業契約的情況有點類似。

契約式設計應用了形式驗證、形式規定與霍爾邏輯的理論。

DbC 的核心思想是對軟體系統中的元素之間相互合作以及「責任」與「權利」的比喻。這種比喻從商業活動中「客戶」與「供應商」達成「契約」而得來。

如果在物件導向程式設計中一個類的函式提供了某種功能,那麼它要:

  1. 期望所有呼叫它的客戶模組都保證一定的進入條件:這就是函式的先決條件 - 客戶的義務和供應商的權利,這樣它就不用去處理不滿足先驗條件的情況。
  2. 保證退出時給出特定的屬性:這就是函式的後置條件 - 供應商的義務,顯然也是客戶的權利。
  3. 在進入時假定,並在退出時保持一些特定的屬性:不變條件。

契約就是這些權利和義務的正式形式。

作為設計者要經常問:

  • 它期望的是什麼?
  • 它要保證的是什麼?
  • 它要保持的是什麼?

不要重複你自己 (Don't repeat yourself, DRY)

這個原則最初由 Andy Hunt 和 Dave Thomas 在他們的書 The Pragmatic Programmer 中提出。因為極限編程方法 (Extreme programming, XP) 的創始者之一 Kent Beck 總結和宣傳而使其廣為人知。此原則又稱為「一次且僅一次」 (Once and only once, OAOO),或「一個規則,實現一次」 (One rule, one place) 是物件導向程式設計中的基本原則,程式設計師的行事準則。旨在軟體開發中,減少重複的資訊。

DRY的原則是「系統中的每一部分,都必須有一個單一的、明確的、權威的代表」,指的是 (由人編寫而非機器生成的) 代碼和測試所構成的系統,必須能夠表達所應表達的內容,但是不能含有任何重複代碼。

通用職責分配軟體模式 (General Responsibility Assignment Software Patterns, GRASP)

物件導向設計和職責分配中的九個基本原則,最早是在 Craig Larman 於 1997 年的 Applying UML and Patterns 書中提到。

GRASP中提到的模式和原則包括有控制器 (controller)、建立者 (creator)、中介 (indirection)、資訊專家 (information expert)、低耦合性 (low coupling)、高內聚性 (high cohesion)、多型 (polymorphism)、保護變化 (protected variations)和純虛構 (pure Fabrication)。這些模式都是針對軟體開發上的一些問題進行解決。發明這些技巧不是為了要創造新的工作方式,而是為在物件導向設計上、舊的、經過測試的程式設計方式建立文件並且標準化。