物件導向設計原則總結

2021-11-05 09:58:43 字數 4849 閱讀 7337

正如牛頓三大定律在經典力學中的位置一樣,「開-閉」原則(open-closed principle)是物件導向的可復用設計(object oriented design或ood)的基石。其他設計原則(黎克特制代換原則、依賴倒轉原則、合成/聚合復用原則、迪公尺特法則、介面隔離原則)是實現「開-閉」原則的手段和工具。

一、「開-閉」原則(open-closed principle,ocp)

1.1「開-閉」原則的定義及優點

1)定義:乙個軟體實體應當對擴充套件開放,對修改關閉( software entities should be open for extension,but closed for modification.)。

即在設計乙個模組的時候,應當使這個模組可以在不被修改的前提下被擴充套件。

2)滿足「開-閉」原則的系統的優點

a)通過擴充套件已有的軟體系統,可以提供新的行為,以滿足對軟體的新需求,使變化中的軟體系統有一定的適應性和靈活性。

b)已有的軟體模組,特別是最重要的抽象層模組不能再修改,這就使變化中的軟體系統有一定的穩定性和延續性。

c)這樣的系統同時滿足了可復用性與可維護性。

1.2如何實現「開-閉」原則

在物件導向設計中,不允許更改的是系統的抽象層,而允許擴充套件的是系統的實現層。換言之,定義乙個一勞永逸的抽象設計層,允許盡可能多的行為在實現層被實現。

解決問題關鍵在於抽象化,抽象化是物件導向設計的第乙個核心本質。

對乙個事物抽象化,實質上是在概括歸納總結它的本質。抽象讓我們抓住最最重要的東西,從更高一層去思考。這降低了思考的複雜度,我們不用同時考慮那麼多的東西。

換言之,我們封裝了事物的本質,看不到任何細節。

在物件導向程式設計中,通過抽象類及介面,規定了具體類的特徵作為抽象層,相對穩定,不需更改,從而滿足「對修改關閉」;而從抽象類匯出的具體類可以改變系統的行為,從而滿足「對擴充套件開放」。

對實體進行擴充套件時,不必改動軟體的源**或者二進位制**。關鍵在於抽象。

1.3對可變性的封裝原則

「開-閉」原則也就是「對可變性的封裝原則」(principle of encapsulation of variation ,evp)。即找到乙個系統的可變因素,將之封裝起來。換言之,在你的設計中什麼可能會發生變化,應使之成為抽象層而封裝,而不是什麼會導致設計改變才封裝。

「對可變性的封裝原則」意味著:

a)一種可變性不應當散落在**的許多角落,而應當被封裝到乙個物件裡面。同一可變性的不同表象意味著同乙個繼承等級結構中的具體子類。因此,此處可以期待繼承關係的出現。

繼承是封裝變化的方法,而不僅僅是從一般的物件生成特殊的物件。

b)一種可變性不應當與另一種可變性混合在一起。作者認為類圖的繼承結構如果超過兩層,很可能意味著兩種不同的可變性混合在了一起。

使用「可變性封裝原則」來進行設計可以使系統遵守「開-閉」原則。

即使無法百分之百的做到「開-閉」原則,但朝這個方向努力,可以顯著改善乙個系統的結構。

二、黎克特制代換原則(liskov substitution principle, lsp)

2.1概念

定義:如果對每乙個型別為t1的物件o1,都有型別為t2 的物件o2,使得以t1定義的所有程式p在所有的物件o1都代換為o2時,程式p的行為沒有變化,那麼型別t2是型別t1的子型別。

即,乙個軟體實體如果使用的是乙個基類的話,那麼一定適用於其子類。而且它覺察不出基類物件和子類物件的區別。也就是說,在軟體裡面,把基類都替換成它的子類,程式的行為沒有變化。

反過來的代換不成立,如果乙個軟體實體使用的是乙個子類的話,那麼它不一定適用於基類。

任何基類可以出現的地方,子類一定可以出現。

基於契約的設計、抽象出公共部分作為抽象基類的設計。

2.2黎克特制代換原則與「開-閉」原則的關係

實現「開-閉」原則的關鍵步驟是抽象化。基類與子類之間的繼承關係就是抽象化的體現。因此黎克特制代換原則是對實現抽象化的具體步驟的規範。

違反黎克特制代換原則意味著違反了「開-閉」原則,反之未必。

三、 依賴倒轉原則(dependence inversion principle, dip)

3.1概念

依賴倒轉原則就是要依賴於抽象,不要依賴於實現。(abstractions should not depend upon details. details should depend upon abstractions.

)要針對介面程式設計,不要針對實現程式設計。(program to an inte***ce, not an implementation.)

也就是說應當使用介面和抽象類進行變數型別宣告、引數型別宣告、方法返還型別說明,以及資料型別的轉換等。而不要用具體類進行變數的型別宣告、引數型別宣告、方法返還型別說明,以及資料型別的轉換等。要保證做到這一點,乙個具體類應當只實現介面和抽象類中宣告過的方法,而不要給出多餘的方法。

傳統的過程性系統的設計辦法傾向於使高層次的模組依賴於低層次的模組,抽象層次依賴於具體層次。倒轉原則就是把這個錯誤的依賴關係倒轉過來。

物件導向設計的重要原則是建立抽象化,並且從抽象化匯出具體化,具體化給出不同的實現。繼承關係就是一種從抽象化到具體化的匯出。

抽象層包含的應該是應用系統的商務邏輯和巨集觀的、對整個系統來說重要的戰略性決定,是必然性的體現。具體層次含有的是一些次要的與實現有關的演算法和邏輯,以及戰術性的決定,帶有相當大的偶然性選擇。具體層次的**是經常變動的,不能避免出現錯誤。

從復用的角度來說,高層次的模組是應當復用的,而且是復用的重點,因為它含有乙個應用系統最重要的巨集觀商務邏輯,是較為穩定的。而在傳統的過程性設計中,復用則側重於具體層次模組的復用。

依賴倒轉原則則是對傳統的過程性設計方法的「倒轉」,是高層次模組復用及其可維護性的有效規範。

特例:物件的建立過程是違背「開—閉」原則以及依賴倒轉原則的,但通過工廠模式,能很好地解決物件建立過程中的依賴倒轉問題。

3.2關係

「開-閉」原則與依賴倒轉原則是目標和手段的關係。如果說開閉原則是目標,依賴倒轉原則是到達"開閉"原則的手段。如果要達到最好的"開閉"原則,就要盡量的遵守依賴倒轉原則,依賴倒轉原則是對"抽象化"的最好規範。

黎克特制代換原則是依賴倒轉原則的基礎,依賴倒轉原則是黎克特制代換原則的重要補充。

3.3耦合(或者依賴)關係的種類:

零耦合(nil coupling)關係:兩個類沒有耦合關係

具體耦合(concrete coupling)關係:發生在兩個具體的(可例項化的)類之間,經由乙個類對另乙個具體類的直接引用造成。

抽象耦合(abstract coupling)關係:發生在乙個具體類和乙個抽象類(或介面)之間,使兩個必須發生關係的類之間存有最大的靈活性。

3.3.1如何把握耦合

我們應該盡可能的避免實現繼承,原因如下:

1 失去靈活性,使用具體類會給底層的修改帶來麻煩。

2 耦合問題,耦合是指兩個實體相互依賴於對方的乙個量度。程式設計師每天都在(有意識地或者無意識地)做出影響耦合的決定:類耦合、api耦合、應用程式耦合等等。

在乙個用擴充套件的繼承實現系統中,派生類是非常緊密的與基類耦合,而且這種緊密的連線可能是被不期望的。如b extends a ,當b不全用a中的所有methods時,這時候,b呼叫的方法可能會產生錯誤!

我們必須客觀的評價耦合度,系統之間不可能總是松耦合的,那樣肯定什麼也做不了。

3.3.2我們決定耦合的程度的依據何在呢?

簡單的說,就是根據需求的穩定性,來決定耦合的程度。對於穩定性高的需求,不容易發生變化的需求,我們完全可以把各類設計成緊耦合的(我們雖然討論類之間的耦合度,但其實功能塊、模組、包之間的耦合度也是一樣的),因為這樣可以提高效率, 而且我們還可以使用一些更好的技術來提高效率或簡化**,例如c# 中的內部類技術。可是,如果需求極有可能變化,我們就需要充分的考慮類之間的耦合問題,我們可以想出各種各樣的辦法來降低耦合程度,但是歸納起來,不外乎增加抽象的層次來隔離不同的類,這個抽象層次可以是抽象的類、具體的類,也可以是介面,或是一組的類。

我們可以用一句話來概括降低耦合度的思想:"針對介面程式設計,而不是針對實現程式設計。

在我們進行編碼的時候,都會留下我們的指紋,如public的多少,**的格式等等。 我們可以耦合度量評估重新構建**的風險。因為重新構建實際上是維護編碼的一種形式,維護中遇到的那些麻煩事在重新構建時同樣會遇到。

我們知道在重新構建之後,最常見的隨機bug大部分都是不當耦合造成的 。

如果不穩定因素越大,它的耦合度也就越大。

某類的不穩定因素=依賴的類個數/被依賴的類個數

依賴的類個數= 在編譯此類的時被編譯的其它類的個數總和

3.3.3怎樣將大系統拆分成小系統

解決這個問題的乙個思路是將許多類集合成乙個更高層次的單位,形成乙個高內聚、低耦合的類的集合,這是我們設計過程中應該著重考慮的問題!

耦合的目標是維護依賴的單向性,有時我們也會需要使用壞的耦合。在這種情況下,應當小心記錄下原因,以幫助日後該**的使用者了解使用耦合真正的原因。

3.4怎樣做到依賴倒轉?

以抽象方式耦合是依賴倒轉原則的關鍵。抽象耦合關係總要涉及具體類從抽象類繼承,並且需要保證在任何引用到基類的地方都可以改換成其子類,因此,黎克特制代換原則是依賴倒轉原則的基礎。

在抽象層次上的耦合雖然有靈活性,但也帶來了額外的複雜性,如果乙個具體類發生變化的可能性非常小,那麼抽象耦合能發揮的好處便十分有限,這時可以用具體耦合反而會更好。

層次化:所有結構良好的物件導向構架都具有清晰的層次定義,每個層次通過乙個定義良好的、受控的介面向外提供一組內聚的服務。

依賴於抽象:建議不依賴於具體類,即程式中所有的依賴關係都應該終止於抽象類或者介面。盡量做到:

1、任何變數都不應該持有乙個指向具體類的指標或者引用。

2、任何類都不應該從具體類派生。

3、任何方法都不應該覆寫它的任何基類中的已經實現的方法。

3.5依賴倒轉原則的優缺點

依賴倒轉原則雖然很強大,但卻最不容易實現。因為依賴倒轉的緣故,物件的建立很可能要使用物件工廠,以避免對具體類的直接引用,此原則的使用可能還會導致產生大量的類,對不熟悉物件導向技術的工程師來說,維護這樣的系統需要較好地理解物件導向設計。

物件導向設計原則的總結

正如牛頓三大定律在經典力學中的位置一樣,開 閉 原則 open closed principle 是物件導向的可復用設計 object oriented design或ood 的基石。其他設計原則 黎克特制代換原則 依賴倒轉原則 合成 聚合復用原則 迪公尺特法則 介面隔離原則 是實現 開 閉 原則的...

物件導向程式設計

第1頁共2頁 9 編寫乙個控制台程式,要求 編寫乙個函式get scores 接受使用者輸入的語文 數學 物理 化學和英語5科成績,在main 中利用get scores 接受輸入,然後計算總成績與平均成績,main 與get scores 之間不得使用全域性變數通訊。10 編寫乙個控制台程式,使用...

物件導向思想

如何理解物件導向程式設計的思想 首先談論一下結構化程式設計的思想,它是一種很傳統的思想,之前在c語言的課程裡我們已經學習過。我認為結構化程式設計最重要的就是把乙個大的程式分成一些小的模組,並按照層次關係逐步去解決這些問題。它是從問題的總體目標開始,抽象低層的細節,先專心構造高層的結構,然後再一層一層...