OO設計原則 -- OO設計的原則及設計過程的全面總結
前面發表了5篇OO設計原則的文章,在這裡我將這個5個原則如何在我們設計過程進行應用進行一下總結,
這是我通過閱讀和學習很多博文和資料後進行的一個梳理和總結,僅供大家來參考。
一.OO(面向物件)的設計基礎
面向物件(OO):就是基於物件概念,以物件為中心,以類和繼承為構造機制,充分利用介面和多型提供靈活性,
來認識、理解、刻劃客觀世界和設計、構建相應的軟體系統。
面向物件的特徵:雖然各種面向物件程式語言相互有別,但都能看到它們對面向物件基本特徵的支援,
即 “抽象、封裝、繼承、多型” :
– 抽象,先不考慮細節
– 封裝,隱藏內部實現
– 繼承,複用現有程式碼
– 多型,改寫物件行為
面向物件設計模式: 是“好的面向物件設計”,所謂“好的面向物件設計”是那些可以滿足“應對變化,
提高複用”的設計。面向物件設計模式描述的是軟體設計,因此它是獨立於程式設計 語言的,但是面向
物件設計模式的最終實現仍然要使用面向物件程式語言來表達。面向物件設計模式不像演算法技巧,
可以照搬照用,它是建立在對“面向物件”純 熟、深入的理解的基礎上的經驗性認識。
上邊就見大的描述一下面向物件和設計模式的概念和關係。我們進行設計的時候,就是充 分的理解和
利用OO的四個基本的特徵來展開設計,所以大家必須在進行設計前,要熟悉和掌握面嚮物件的技術,
在這就不詳細介紹了,而對於設計模式是給我們提供了設計時的參考模型,而掌握面向物件設計模式的
前提是首先掌握“面向物件”技術。
二.OO(面向物件)的設計目標
※可擴充套件性Extensibility:有了新的需求,新的效能可以容易新增到系統中,不影響現有的效能,也不會帶來新的缺陷。
※可修改性Flexibility:系統一部分的程式碼要修改時不會破壞系統的現有結構,也不會影響到其它的部分。
※可替換性Pluggability:可以將系統中的某些程式碼替換為相同介面的其它類,不會影響到系統。
三.OO設計的5大原則及其之間的關係
3.1 OO設計原則的總結
關於OO設計的5個原則,在前面的文章裡,每個都有一篇詳細的說明,
在這裡就不詳細解釋了,以下簡單總結一下。
※單一職責原則:就一個類而言,應該僅有一個引起它變化的原因。
單一是一個類的優良設計。交雜不清的職責將使得程式碼看起來特別彆扭牽一髮而動全身,
有失美感和必然導致醜陋的系統錯誤風險。
※開放封閉原則:是說軟體實體(類、模組、函式等等)應該可以擴充套件但不可修改。
實現開開放封閉原則的核心思想就是對抽象程式設計,而不對具體程式設計,因為抽象相對穩定。
讓類依賴於固定的抽象,所以修改就是封閉的;而通過面向物件的繼承和多型機制,
又可以實現對抽象類的繼承,通過覆寫其方法來改變固有行為,實現新的拓展方法,所以就是開放的。
“需求總是變化”沒有不變的軟體,所以就需要用封閉開放原則來封閉變化滿足需求,
同時還能保持軟體內部的封裝體系穩定,不被需求的變化影響。
※依賴倒置原則:依賴抽象,不要依賴具體。
抽 象的穩定性決定了系統的穩定性,因為抽象是不變的,依賴於抽象是面向物件設計的精髓,
也是依賴倒置原則的核心。依賴於抽象是一個通用的原則,而某些時候依 賴於細節則是在所難免的,
必須權衡在抽象和具體之間的取捨,方法不是一層不變的。依賴於抽象,就是對介面程式設計,不要對實現程式設計。
※里氏代換原則:子型別必須能夠替換到他們的父型別。
Liskov 替換原則,主要著眼於對抽象和多型建立在繼承的基礎上,因此只有遵循了Liskov替換原則,
才能保證繼承複用是可靠地。實現的方法是面向介面程式設計:將公 共部分抽象為基類介面或抽象類,
通過ExtractAbstractClass,在子類中通過覆寫父類的方法實現新的方式支援同樣的職責。Liskov替 換原則能夠
保證系統具有良好的拓展性,同時實現基於多型的抽象機制,能夠減少程式碼冗餘,避免執行期的型別判別。
※介面隔離原則: 多個和客戶相關的介面要好於一個通用介面。
分離的手段主要有以下兩種:1、委託分離,通過增加一個新的型別來委託客戶的請求,隔離客戶和介面的直接依賴,
但是會增加系統的開銷。2、多重繼承分離,通過介面多繼承來實現客戶的需求,這種方式是較好的。
下邊是前面沒有提到過的兩個原則,也是設計時要考慮的重要原則。
※迪米特法則:不相互直接通訊的類之間,不要直接發生相互作用。
如果兩個類不必彼此直接通訊,那麼這兩個類就不應當發生直接的相互作用。如果一個類需要呼叫領一個類的
某個方法話,可以通過第三者轉發這個呼叫。迪米特法則首先強調的前提是在類的設計上,每一類都應當儘量
降低成員的訪問許可權。它的根本思想是強調類之間的鬆耦合。
※合成/聚合複用原則:儘量使用合成/聚合,儘量不要使用繼承。
合 成(Composition)和聚合(Aggregation)都是關聯的特殊種類,聚合表示一種弱的擁有關係;
合成這是一種強的擁有關係,體現了嚴格的部分和整體的關係,部分和整體的生命週期一樣。
優先使用合成或聚合原則將有助於保持每個類被封裝,並被集中在單個任務上。這樣類和類繼承
層次會保持較小規 模,並且不太可能增長為不可控制的龐然大物
3.2 OO設計原則之間的關係
1. 實現“開-閉”原則的關鍵步驟是抽象化。基類與子類之間的繼承關係就是抽象化的體現。
因此里氏代換原則是對實現抽象化的具體步驟的規範。
違反里氏代換原則意味著違反了“開-閉”原則,反之未必。
2. “開-閉”原則與依賴倒轉原則是目標和手段的關係。如果說開閉原則是目標,依賴倒轉原則
是到達"開閉"原則的手段。如果要達到最好的"開閉"原則,就要儘量的遵守依賴倒轉原則,
依賴倒轉原則是對"抽象化"的最好規範。
3. 里氏代換原則是依賴倒轉原則的基礎,依賴倒轉原則是里氏代換原則的重要補充。
4. 介面分離原則也是確保“開-閉”原則的一個重要手段。
5. 對於單一職責原則,個人認為儘量做到為好,職責越單一,“開-閉”和里氏代換越容易實現。
四.OO設計原則和目標的關係
1.可擴充套件性Extensibility :允許一個具有同樣介面的新類替代舊類,是對抽象介面的複用。
客戶端依賴於抽象介面,而不是一個具體實現類,使得這個具體類可以被別的具體類替換,
而不影響客戶端。以下原則實現可擴充套件性。
※開/閉原則
※里氏替換原則
※依賴倒轉原則
※合成/聚合複用原則
2.可修改性Flexibility:模組相對獨立,通訊儘可能少。這樣當一個模組修改時,對別的模組的影響很小。
以下原則實現可修改性。
※開/閉原則
※迪米特法則
※介面隔離原則
3、可替換性Pluggability:當一部分不再滿足需要時,可以將舊的部分拔出,新的部分插入。
以下原則實現可替換性。
※開/閉原則
※里氏代換原則
※依賴倒轉原則
※合成/聚合複用原則
五.OO(面向物件)的設計過程
1. 分析式樣,進行機能分類。
2. 根據機能進行類的抽象。
※ 類的抽象 - 在這裡步裡,我們可以根據 “單一職責原則”,進行類的具體抽象。
儘量做到,類的功能單一和清晰化。
※ 封裝變化點– 使用封裝來建立物件之間的分界層,讓設計者可以在分界層的一側進行修改,
而不會對另一側產生不良的影響,從而實現層次間的鬆耦合。
3. 設計抽象基類和介面類。
※ 在進行基本的基類的抽象和介面定義時,要遵照“介面分離原則”進行介面的抽象。
※ 在設計介面和基類時,不要總是關注細節,要記住針對介面程式設計,而不是針對實現程式設計。
※ 對於抽象的基類和派生類之間要做到“里氏替換原則”的要求。
4.確定類間的耦合關係。
4.1 決定耦合的程度的依據何在呢?
※ 簡單的說,就是根據需求的穩定性,來決定耦合的程度。
※ 對於穩定性高的需求,不容易發生變化的需求,我們完全可以把各類設計成緊耦合的,
因為這樣可以提高效率,而且我們還可以使用一些更好的技術來提高效率或簡化程式碼。
※ 如 果需求極有可能變化,我們就需要充分的考慮類之間的耦合問題,我們可以想出各種
各樣的辦法來降低耦合程度,但是歸納起來,不外乎增加抽象的層次來隔離不同的類,
這個抽象層次可以是抽象的類、具體的類,也可以是介面,或是一組的類。
我們可以用一句話來概括降低耦合度的思想:"針對接 口程式設計,而不是針對實現程式設計。
※ 在決定類的耦合關係時,儘量考慮“迪米特法則”和“合成/聚合複用原則”。
4.2 怎樣做到依賴倒轉?
※ 以抽象方式耦合是依賴倒轉原則的關鍵。抽象耦合關係總要涉及具體類從抽象類繼承,
並且需要保證在任何引用到基類的地方都可以改換成其子類,因此,里氏代換原則是依賴倒轉原則的基礎。
※ 依賴於抽象:建議不依賴於具體類,即程式中所有的依賴關係都應該終止於抽象類或者介面。儘量做到:
(1)任何變數都不應該持有一個指向具體類的指標或者引用。
(2)任何類都不應該從具體類派生。
(3)任何方法都不應該覆寫它的任何基類中的已經實現的方法。
5.運用OO設計的5大原則來對設計進行進一步的優化
※ 對於類的抽象和職能,是否滿足“單一職責原則”
※ 對於繼承關係和引用基類的地方,是否滿足“里氏代換原則”和“依賴倒置原則”
※ 對於介面和基類,是否“介面隔離原則”
※ 總體上是否滿足“開-閉原則”
總體上說,在面向物件設計時,要充分考慮設計的5大原則,但不是強求的,一味的追求滿足原則也可能會
導致設計出的系統在效能和資源上的消耗,可以根據具體的情況來具體的分析和設計。