|
在設(shè)計(jì)面向?qū)ο蟮某绦虻臅r(shí),模式不是一定要套的,但是有一些原則最好是遵守。這些原則已知的有七個(gè),包括:開(kāi)閉原則、里氏代換原則、依賴(lài)倒轉(zhuǎn)原則、合成/聚合復(fù)用原則、迪米特法則、接口隔離原則,單一職責(zé)原則。
原則簡(jiǎn)介
其中Robert C. Martin引入了SOLID的說(shuō)法,包括了其中的五個(gè)原則。另外兩個(gè),這里把他們算成額外的兩個(gè)規(guī)則。具體如下。
S . Single Responsibility Principle - SRP
An object should have only a single responsibility.
O . Open-Closed Principle - OCP
Software entities should be opened for extension, but closed for modification.
L . Liskvo Substitution Principle - LSP
If S is a subtype of T, the objects of T in a program may be replaced by objects of Type S.
I . Interface Segregation Principle - ISP
many client specific interfaces are better than one general purpose interface.
D . Dependency Inversion Principle - DIP
Depend upon abstractions. Do not depend upon concretions.
Program to an interface, not an implementation.
Ex1 . Law of Demeter - LoD
-- Principle of Least Knowledge
A given object should assume as little as possible about the structure or properties of anything else.
Ex2 . Composite/Aggregate Reuse Principle - CARP
Use most composition and aggregation, less inheritance.
按照個(gè)人理解,把這七個(gè)原則分成了以下兩個(gè)部分。
Design Method: SRP ISP DIP CARP
Design Goal: OCP LSP LoD
各規(guī)則詳細(xì)(本部分為轉(zhuǎn)載)
正如牛頓三大定律在經(jīng)典力學(xué)中的位置一樣,“開(kāi)-閉”原則(Open-Closed Principle)是面向?qū)ο蟮目蓮?fù)用設(shè)計(jì)(Object Oriented Design或OOD)的基石。其他設(shè)計(jì)原則(里氏代換原則、依賴(lài)倒轉(zhuǎn)原則、合成/聚合復(fù)用原則、迪米特法則、接口隔離原則)是實(shí)現(xiàn)“開(kāi)-閉”原則的手段和工具。
一、“開(kāi)- 閉”原則(Open-Closed Principle,OCP )
1.1 “開(kāi)- 閉” 原則的定義及優(yōu)點(diǎn)
1)定義:一個(gè)軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉( Software entities should be open for extension, but closed for modification.)。即在設(shè)計(jì)一個(gè)模塊的時(shí)候,應(yīng)當(dāng)使這個(gè)模塊可以在不被修改的前提下被擴(kuò)展。
2)滿(mǎn)足“開(kāi)-閉”原則的系統(tǒng)的優(yōu)點(diǎn)
a) 通過(guò)擴(kuò)展已有的軟件系統(tǒng),可以提供新的行為,以滿(mǎn)足對(duì)軟件的新需求,使變化中的軟件系統(tǒng)有一定的適應(yīng)性和靈活性。
b) 已有的軟件模塊,特別是最重要的抽象層模塊不能再修改,這就使變化中的軟件系統(tǒng)有一定的穩(wěn)定性和延續(xù)性。
c) 這樣的系統(tǒng)同時(shí)滿(mǎn)足了可復(fù)用性與可維護(hù)性。
1.2 如何實(shí)現(xiàn)“開(kāi)-閉” 原則
在面向?qū)ο笤O(shè)計(jì)中,不允許更改的是系統(tǒng)的抽象層,而允許擴(kuò)展的是系統(tǒng)的實(shí)現(xiàn)層。換言之,定義一個(gè)一勞永逸的抽象設(shè)計(jì)層,允許盡可能多的行為在實(shí)現(xiàn)層被實(shí)現(xiàn)。
解決問(wèn)題關(guān)鍵在于抽象化,抽象化是面向?qū)ο笤O(shè)計(jì)的第一個(gè)核心本質(zhì)。
對(duì)一個(gè)事物抽象化,實(shí)質(zhì)上是在概括歸納總結(jié)它的本質(zhì)。抽象讓我們抓住最最重要的東西, 從更高一層去思考。這降低了思考的復(fù)雜度,我們不用同時(shí)考慮那么多的東西。換言之,我們封裝了事物的本質(zhì),看不到任何細(xì)節(jié)。
在面向?qū)ο缶幊讨校ㄟ^(guò)抽象類(lèi)及接口,規(guī)定了具體類(lèi)的特征作為抽象層,相對(duì)穩(wěn)定,不需更改,從而滿(mǎn)足“對(duì)修改關(guān)閉”;而從抽象類(lèi)導(dǎo)出的具體類(lèi)可以改變系統(tǒng)的行為,從而滿(mǎn)足“對(duì)擴(kuò)展開(kāi)放”。
對(duì)實(shí)體進(jìn)行擴(kuò)展時(shí),不必改動(dòng)軟件的源代碼或者二進(jìn)制代碼。關(guān)鍵在于抽象。
1.3 對(duì)可變性的封裝原則
“開(kāi)-閉”原則也就是“對(duì)可變性的封裝原則”(Principle of Encapsulation of Variation ,EVP)。即找到一個(gè)系統(tǒng)的可變因素,將之封裝起來(lái)。換言之,在你的設(shè)計(jì)中什么可能會(huì)發(fā)生變化,應(yīng)使之成為抽象層而封裝,而不是什么會(huì)導(dǎo)致設(shè)計(jì)改變才封裝。
“對(duì)可變性的封裝原則”意味著:
a) 一種可變性不應(yīng)當(dāng)散落在代碼的許多角落,而應(yīng)當(dāng)被封裝到一個(gè)對(duì)象里面。同一可變性的不同表象意味著同一個(gè)繼承等級(jí)結(jié)構(gòu)中的具體子類(lèi)。因此,此處可以期待繼承關(guān)系的出現(xiàn)。繼承是封裝變化的方法,而不僅僅是從一般的對(duì)象生成特殊的對(duì)象。
b) 一種可變性不應(yīng)當(dāng)與另一種可變性混合在一起。作者認(rèn)為類(lèi)圖的繼承結(jié)構(gòu)如果超過(guò)兩層,很可能意味著兩種不同的可變性混合在了一起。
使用“可變性封裝原則”來(lái)進(jìn)行設(shè)計(jì)可以使系統(tǒng)遵守“開(kāi)-閉”原則。
即使無(wú)法百分之百的做到“開(kāi)-閉”原則,但朝這個(gè)方向努力,可以顯著改善一個(gè)系統(tǒng)的結(jié)構(gòu)。
二、里氏代換原則(Liskov Substitution Principle, LSP)
2.1 概念
定義:如果對(duì)每一個(gè)類(lèi)型為 T1 的對(duì)象 O1 ,都有類(lèi)型為 T2 的對(duì)象 O2 ,使得以 T1 定義的所有程序 P 在所有的對(duì)象 O1 都代換為 O2 時(shí),程序 P 的行為沒(méi)有變化,那么類(lèi)型 T2 是類(lèi)型 T1 的子類(lèi)型。
即,一個(gè)軟件實(shí)體如果使用的是一個(gè)基類(lèi)的話(huà),那么一定適用于其子類(lèi)。而且它覺(jué)察不出基類(lèi)對(duì)象和子類(lèi)對(duì)象的區(qū)別。 也就是說(shuō),在軟件里面,把基類(lèi)都替換成它的子類(lèi),程序的行為沒(méi)有變化。
反過(guò)來(lái)的代換不成立,如果一個(gè)軟件實(shí)體使用的是一個(gè)子類(lèi)的話(huà),那么它不一定適用于基類(lèi)。
任何基類(lèi)可以出現(xiàn)的地方,子類(lèi)一定可以出現(xiàn)。
基于契約的設(shè)計(jì)、抽象出公共部分作為抽象基類(lèi)的設(shè)計(jì)。
2.2 里氏代換原則與“開(kāi)- 閉” 原則的關(guān)系
實(shí)現(xiàn)“開(kāi)-閉”原則的關(guān)鍵步驟是抽象化。基類(lèi)與子類(lèi)之間的繼承關(guān)系就是抽象化的體現(xiàn)。因此里氏代換原則是對(duì)實(shí)現(xiàn)抽象化的具體步驟的規(guī)范。
違反里氏代換原則意味著違反了“開(kāi)-閉”原則,反之未必。
三、 依賴(lài)倒轉(zhuǎn)原則 ( dependence inversion principle, DIP )
3.1 概念
依賴(lài)倒轉(zhuǎn)原則就是要依賴(lài)于抽象,不要依賴(lài)于實(shí)現(xiàn)。(Abstractions should not depend upon details. Details should depend upon abstractions.)要針對(duì)接口編程,不要針對(duì)實(shí)現(xiàn)編程。(Program to an interface, not an implementation.)
也就是說(shuō)應(yīng)當(dāng)使用接口和抽象類(lèi)進(jìn)行變量類(lèi)型聲明、參數(shù)類(lèi)型聲明、方法返還類(lèi)型說(shuō)明,以及數(shù)據(jù)類(lèi)型的轉(zhuǎn)換等。而不要用具體類(lèi)進(jìn)行變量的類(lèi)型聲明、參數(shù)類(lèi)型聲明、方法返還類(lèi)型說(shuō)明,以及數(shù)據(jù)類(lèi)型的轉(zhuǎn)換等。要保證做到這一點(diǎn),一個(gè)具體類(lèi)應(yīng)當(dāng)只實(shí)現(xiàn)接口和抽象類(lèi)中聲明過(guò)的方法,而不要給出多余的方法。
傳統(tǒng)的過(guò)程性系統(tǒng)的設(shè)計(jì)辦法傾向于使高層次的模塊依賴(lài)于低層次的模塊,抽象層次依賴(lài)于具體層次。倒轉(zhuǎn)原則就是把這個(gè)錯(cuò)誤的依賴(lài)關(guān)系倒轉(zhuǎn)過(guò)來(lái)。
面向?qū)ο笤O(shè)計(jì)的重要原則是創(chuàng)建抽象化,并且從抽象化導(dǎo)出具體化,具體化給出不同的實(shí)現(xiàn)。繼承關(guān)系就是一種從抽象化到具體化的導(dǎo)出。
抽象層包含的應(yīng)該是應(yīng)用系統(tǒng)的商務(wù)邏輯和宏觀(guān)的、對(duì)整個(gè)系統(tǒng)來(lái)說(shuō)重要的戰(zhàn)略性決定,是必然性的體現(xiàn)。具體層次含有的是一些次要的與實(shí)現(xiàn)有關(guān)的算法和邏輯,以及戰(zhàn)術(shù)性的決定,帶有相當(dāng)大的偶然性選擇。具體層次的代碼是經(jīng)常變動(dòng)的,不能避免出現(xiàn)錯(cuò)誤。
從復(fù)用的角度來(lái)說(shuō),高層次的模塊是應(yīng)當(dāng)復(fù)用的,而且是復(fù)用的重點(diǎn),因?yàn)樗幸粋€(gè)應(yīng)用系統(tǒng)最重要的宏觀(guān)商務(wù)邏輯,是較為穩(wěn)定的。而在傳統(tǒng)的過(guò)程性設(shè)計(jì)中,復(fù)用則側(cè)重于具體層次模塊的復(fù)用。
依賴(lài)倒轉(zhuǎn)原則則是對(duì)傳統(tǒng)的過(guò)程性設(shè)計(jì)方法的“倒轉(zhuǎn)”,是高層次模塊復(fù)用及其可維護(hù)性的有效規(guī)范。
特例:對(duì)象的創(chuàng)建過(guò)程是違背“開(kāi)—閉”原則以及依賴(lài)倒轉(zhuǎn)原則的,但通過(guò)工廠(chǎng)模式,能很好地解決對(duì)象創(chuàng)建過(guò)程中的依賴(lài)倒轉(zhuǎn)問(wèn)題。
3.2 關(guān)系
“開(kāi)-閉”原則與依賴(lài)倒轉(zhuǎn)原則是目標(biāo)和手段的關(guān)系。如果說(shuō)開(kāi)閉原則是目標(biāo),依賴(lài)倒轉(zhuǎn)原則是到達(dá)"開(kāi)閉"原則的手段。如果要達(dá)到最好的"開(kāi)閉"原則,就要盡量的遵守依賴(lài)倒轉(zhuǎn)原則,依賴(lài)倒轉(zhuǎn)原則是對(duì)"抽象化"的最好規(guī)范。
里氏代換原則是依賴(lài)倒轉(zhuǎn)原則的基礎(chǔ),依賴(lài)倒轉(zhuǎn)原則是里氏代換原則的重要補(bǔ)充。
3.3 耦合(或者依賴(lài))關(guān)系的種類(lèi):
零耦合(Nil Coupling)關(guān)系:兩個(gè)類(lèi)沒(méi)有耦合關(guān)系。
具體耦合(Concrete Coupling)關(guān)系:發(fā)生在兩個(gè)具體的(可實(shí)例化的)類(lèi)之間,經(jīng)由一個(gè)類(lèi)對(duì)另一個(gè)具體類(lèi)的直接引用造成。
抽象耦合(Abstract Coupling)關(guān)系:發(fā)生在一個(gè)具體類(lèi)和一個(gè)抽象類(lèi)(或接口)之間,使兩個(gè)必須發(fā)生關(guān)系的類(lèi)之間存有最大的靈活性。
3.3.1 如何把握耦合
我們應(yīng)該盡可能的避免實(shí)現(xiàn)繼承,原因如下:
1) 失去靈活性,使用具體類(lèi)會(huì)給底層的修改帶來(lái)麻煩。
2) 耦合問(wèn)題,耦合是指兩個(gè)實(shí)體相互依賴(lài)于對(duì)方的一個(gè)量度。程序員每天都在(有意識(shí)地或者無(wú)意識(shí)地)做出影響耦合的決定:類(lèi)耦合、API耦合、應(yīng)用程序耦合等等。在一個(gè)用擴(kuò)展的繼承實(shí)現(xiàn)系統(tǒng)中,派生類(lèi)是非常緊密的與基類(lèi)耦合,而且這種緊密的連接可能是被不期望的。如B extends A ,當(dāng)B不全用A中的所有methods時(shí),這時(shí)候,B調(diào)用的方法可能會(huì)產(chǎn)生錯(cuò)誤!
我們必須客觀(guān)的評(píng)價(jià)耦合度,系統(tǒng)之間不可能總是松耦合的,那樣肯定什么也做不了。
3.3.2 我們決定耦合的程度的依據(jù)何在呢 ?
簡(jiǎn)單的說(shuō),就是根據(jù)需求的穩(wěn)定性,來(lái)決定耦合的程度。對(duì)于穩(wěn)定性高的需求,不容易發(fā)生變化的需求,我們完全可以把各類(lèi)設(shè)計(jì)成緊耦合的(我們雖然討論類(lèi)之間的耦合度,但其實(shí)功能塊、模塊、包之間的耦合度也是一樣的),因?yàn)檫@樣可以提高效率, 而且我們還可以使用一些更好的技術(shù)來(lái)提高效率或簡(jiǎn)化代碼,例如c#中的內(nèi)部類(lèi)技術(shù)。可是,如果需求極有可能變化,我們就需要充分的考慮類(lèi)之間的耦合問(wèn)題,我們可以想出各種各樣的辦法來(lái)降低耦合程度,但是歸納起來(lái),不外乎增加抽象的層次來(lái)隔離不同的類(lèi),這個(gè)抽象層次可以是抽象的類(lèi)、具體的類(lèi),也可以是接口,或是一組的類(lèi)。我們可以用一句話(huà)來(lái)概括降低耦合度的思想:“針對(duì)接口編程,而不是針對(duì)實(shí)現(xiàn)編程。”
在我們進(jìn)行編碼的時(shí)候,都會(huì)留下我們的指紋,如public的多少,代碼的格式等等。 我們可以耦合度量評(píng)估重新構(gòu)建代碼的風(fēng)險(xiǎn)。因?yàn)橹匦聵?gòu)建實(shí)際上是維護(hù)編碼的一種形式,維護(hù)中遇到的那些麻煩事在重新構(gòu)建時(shí)同樣會(huì)遇到。我們知道在重新構(gòu)建 之后,最常見(jiàn)的隨機(jī)bug大部分都是不當(dāng)耦合造成的 。
如果不穩(wěn)定因素越大,它的耦合度也就越大。
某類(lèi)的不穩(wěn)定因素=依賴(lài)的類(lèi)個(gè)數(shù)/被依賴(lài)的類(lèi)個(gè)數(shù)
依賴(lài)的類(lèi)個(gè)數(shù)= 在編譯此類(lèi)的時(shí)被編譯的其它類(lèi)的個(gè)數(shù)總和
3.3.3 怎樣將大系統(tǒng)拆分成小系統(tǒng)
解決這個(gè)問(wèn)題的一個(gè)思路是將許多類(lèi)集合成一個(gè)更高層次的單位,形成一個(gè)高內(nèi)聚、低耦合的類(lèi)的集合,這是我們?cè)O(shè)計(jì)過(guò)程中應(yīng)該著重考慮的問(wèn)題!
耦合的目標(biāo)是維護(hù)依賴(lài)的單向性,有時(shí)我們也會(huì)需要使用壞的耦合。在這種情況下,應(yīng)當(dāng)小心記錄下原因,以幫助日后該代碼的用戶(hù)了解使用耦合真正的原因。
3.4 怎樣做到依賴(lài)倒轉(zhuǎn)?
以抽象方式耦合是依賴(lài)倒轉(zhuǎn)原則的關(guān)鍵。抽象耦合關(guān)系總要涉及具體類(lèi)從抽象類(lèi)繼承,并且需要保證在任何引用到基類(lèi)的地方都可以改換成其子類(lèi),因此,里氏代換原則是依賴(lài)倒轉(zhuǎn)原則的基礎(chǔ)。
在抽象層次上的耦合雖然有靈活性,但也帶來(lái)了額外的復(fù)雜性,如果一個(gè)具體類(lèi)發(fā)生變化的可能性非常小,那么抽象耦合能發(fā)揮的好處便十分有限,這時(shí)可以用具體耦合反而會(huì)更好。
層次化:所有結(jié)構(gòu)良好的面向?qū)ο髽?gòu)架都具有清晰的層次定義,每個(gè)層次通過(guò)一個(gè)定義良好的、受控的接口向外提供一組內(nèi)聚的服務(wù)。
依賴(lài)于抽象:建議不依賴(lài)于具體類(lèi),即程序中所有的依賴(lài)關(guān)系都應(yīng)該終止于抽象類(lèi)或者接口。盡量做到:
1、任何變量都不應(yīng)該持有一個(gè)指向具體類(lèi)的指針或者引用。
2、任何類(lèi)都不應(yīng)該從具體類(lèi)派生。
3、任何方法都不應(yīng)該覆寫(xiě)它的任何基類(lèi)中的已經(jīng)實(shí)現(xiàn)的方法。
3.5 依賴(lài)倒轉(zhuǎn)原則的優(yōu)缺點(diǎn)
依賴(lài)倒轉(zhuǎn)原則雖然很強(qiáng)大,但卻最不容易實(shí)現(xiàn)。因?yàn)橐蕾?lài)倒轉(zhuǎn)的緣故,對(duì)象的創(chuàng)建很可能要使用對(duì)象工廠(chǎng),以避免對(duì)具體類(lèi)的直接引用,此原則的使用可能還會(huì)導(dǎo)致產(chǎn)生大量的類(lèi),對(duì)不熟悉面向?qū)ο蠹夹g(shù)的工程師來(lái)說(shuō),維護(hù)這樣的系統(tǒng)需要較好地理解面向?qū)ο笤O(shè)計(jì)。
依賴(lài)倒轉(zhuǎn)原則假定所有的具體類(lèi)都是會(huì)變化的,這也不總是正確。有一些具體類(lèi)可能是相當(dāng) 穩(wěn)定,不會(huì)變化的,使用這個(gè)具體類(lèi)實(shí)例的應(yīng)用完全可以依賴(lài)于這個(gè)具體類(lèi)型,而不必為此創(chuàng)建一個(gè)抽象類(lèi)型。
四、合成/聚合復(fù)用原則( Composite/Aggregate Reuse Principle 或 CARP )
4.1 概念
定義:在一個(gè)新的對(duì)象里面使用一些已有的對(duì)象,使之成為新對(duì)象的一部分;新的對(duì)象通過(guò)向這些對(duì)象的委派達(dá)到復(fù)用這些對(duì)象的目的。
應(yīng)首先使用合成/聚合,合成/聚合則使系統(tǒng)靈活,其次才考慮繼承,達(dá)到復(fù)用的目的。而使用繼承時(shí),要嚴(yán)格遵循里氏代換原則。有效地使用繼承會(huì)有助于對(duì)問(wèn)題的理解,降低復(fù)雜度,而濫用繼承會(huì)增加系統(tǒng)構(gòu)建、維護(hù)時(shí)的難度及系統(tǒng)的復(fù)雜度。
如果兩個(gè)類(lèi)是“Has-a”關(guān)系應(yīng)使用合成、聚合,如果是“Is-a”關(guān)系可使用繼 承。"Is-A"是嚴(yán)格的分類(lèi)學(xué)意義上定義,意思是一個(gè)類(lèi)是另一個(gè)類(lèi)的"一種"。而"Has-A"則不同,它表示某一個(gè)角色具有某一項(xiàng)責(zé)任。
4.2 什么是合成?什么是 聚合?
合成(Composition)和聚合(Aggregation)都是關(guān)聯(lián) (Association)的特殊種類(lèi)。
聚合表示整體和部分的關(guān)系,表示“擁有”。如奔馳S360汽車(chē),對(duì)奔馳S360引擎、奔馳S360輪胎的關(guān)系是聚合關(guān)系,離開(kāi)了奔馳S360汽車(chē),引擎、輪胎就失去了存在的意義。在設(shè)計(jì)中, 聚合不應(yīng)該頻繁出現(xiàn),這樣會(huì)增大設(shè)計(jì)的耦合度。
合成則是一種更強(qiáng)的“擁有”,部分和整體的生命周期一樣。合成的新的對(duì)象完全支 配其組成部分,包括它們的創(chuàng)建和湮滅等。一個(gè)合成關(guān)系的成分對(duì)象是不能與另一個(gè)合成關(guān)系共享的。
換句話(huà)說(shuō),合成是值的聚合(Aggregation by Value),而一般說(shuō)的聚合是引用的聚合(Aggregation by Reference)。
明白了合成和聚合關(guān)系,再來(lái)理解合成/聚合原則應(yīng)該就清楚了,要避免在系統(tǒng)設(shè)計(jì)中出現(xiàn),一個(gè)類(lèi)的繼承層次超過(guò)3層,則需考慮重構(gòu)代碼,或者重新設(shè)計(jì)結(jié)構(gòu)。當(dāng)然最好的辦法就是考慮使用合成/聚合原則。
4.3 通過(guò)合成/聚合的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1) 新對(duì)象存取成分對(duì)象的唯一方法是通過(guò)成分對(duì)象的接口。
2) 這種復(fù)用是黑箱復(fù)用,因?yàn)槌煞謱?duì)象的內(nèi)部細(xì)節(jié)是新對(duì)象所看不見(jiàn)的。
3) 這種復(fù)用支持包裝。
4) 這種復(fù)用所需的依賴(lài)較少。
5) 每一個(gè)新的類(lèi)可以將焦點(diǎn)集中在一個(gè)任務(wù)上。
6) 這種復(fù)用可以在運(yùn)行時(shí)間內(nèi)動(dòng)態(tài)進(jìn)行,新對(duì)象可以動(dòng)態(tài)的引用與成分對(duì)象類(lèi)型相同的對(duì)象。
7) 作為復(fù)用手段可以應(yīng)用到幾乎任何環(huán)境中去。
缺點(diǎn):就是系統(tǒng)中會(huì)有較多的對(duì)象需要管理。
4.4 通過(guò)繼承來(lái)進(jìn)行復(fù)用的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1) 新的實(shí)現(xiàn)較為容易,因?yàn)槌?lèi)的大部分功能可以通過(guò)繼承的關(guān)系自動(dòng)進(jìn)入子類(lèi)。
2) 修改和擴(kuò)展繼承而來(lái)的實(shí)現(xiàn)較為容易。
缺點(diǎn) :
1) 繼承復(fù)用破壞封裝,因?yàn)槔^承將超類(lèi)的實(shí)現(xiàn)細(xì)節(jié)暴露給子類(lèi)。由于超類(lèi)的內(nèi)部細(xì)節(jié)常常是對(duì)于子類(lèi)透明的,所以這種復(fù)用是透明的復(fù)用,又稱(chēng)“白箱”復(fù)用。
2) 如果超類(lèi)發(fā)生改變,那么子類(lèi)的實(shí)現(xiàn)也不得不發(fā)生改變。
3) 從超類(lèi)繼承而來(lái)的實(shí)現(xiàn)是靜態(tài)的,不可能在運(yùn)行時(shí)間內(nèi)發(fā)生改變,沒(méi)有足夠的靈活性。
4) 繼承只能在有限的環(huán)境中使用。
五、 迪米特法則( Law of Demeter ,LoD )
5.1 概述
定義:一個(gè)軟件實(shí)體應(yīng)當(dāng)盡可能少的與其他實(shí)體發(fā)生相互作用。
這樣,當(dāng)一個(gè)模塊修改時(shí),就會(huì)盡量少的影響其他的模塊。擴(kuò)展會(huì)相對(duì)容易。
這是對(duì)軟件實(shí)體之間通信的限制。它要求限制軟件實(shí)體之間通信的寬度和深度。
5.2 迪米特法則的其他表述
1)只與你直接的朋友們通信。
2)不要跟“陌生人”說(shuō)話(huà)。
3)每一個(gè)軟件單位對(duì)其他的單位都只有最少的知識(shí),而且局限于那些與本單位密切相關(guān)的軟件單位。
5.3 狹義的迪米特法則
如果兩個(gè)類(lèi)不必彼此直接通信,那么這兩個(gè)類(lèi)就不應(yīng)當(dāng)發(fā)生直接的相互作用。如果其中的一個(gè)類(lèi)需要調(diào)用另一個(gè)類(lèi)的某一個(gè)方法的話(huà),可以通過(guò)第三者轉(zhuǎn)發(fā)這個(gè)調(diào)用。
朋友圈的確定,“朋友”條件:
1)當(dāng)前對(duì)象本身(this)
2)以參量形式傳入到當(dāng)前對(duì)象方法中的對(duì)象
3)當(dāng)前對(duì)象的實(shí)例變量直接引用的對(duì)象
4)當(dāng)前對(duì)象的實(shí)例變量如果是一個(gè)聚集,那么聚集中的元素也都是朋友
5)當(dāng)前對(duì)象所創(chuàng)建的對(duì)象
任何一個(gè)對(duì)象,如果滿(mǎn)足上面的條件之一,就是當(dāng)前對(duì)象的“朋友”;否則就是“陌生 人”。
缺點(diǎn):會(huì)在系統(tǒng)里造出大量的小方法,散落在系統(tǒng)的各個(gè)角落。
與依賴(lài)倒轉(zhuǎn)原則互補(bǔ)使用。
5.4 狹義的迪米特法則的缺點(diǎn):
在系統(tǒng)里造出大量的小方法,這些方法僅僅是傳遞間接的調(diào)用,與系統(tǒng)的商務(wù)邏輯無(wú)關(guān)。
遵循類(lèi)之間的迪米特法則會(huì)是一個(gè)系統(tǒng)的局部設(shè)計(jì)簡(jiǎn)化,因?yàn)槊恳粋€(gè)局部都不會(huì)和遠(yuǎn)距離的對(duì)象有直接的關(guān)聯(lián)。但是,這也會(huì)造成系統(tǒng)的不同模塊之間的通信效率降低,也會(huì)使系統(tǒng)的不同模塊之間不容易協(xié)調(diào)。
5.5 迪米特法則與設(shè)計(jì)模式
門(mén)面(外觀(guān))模式和調(diào)停者(中介者)模式實(shí)際上就是迪米特法則的具體應(yīng)用。
5.6 廣義的迪米特法則
迪米特法則的主要用意是控制信息的過(guò)載。在將迪米特法則運(yùn)用到系統(tǒng)設(shè)計(jì)中時(shí),要注意下面的幾點(diǎn):
1)在類(lèi)的劃分上,應(yīng)當(dāng)創(chuàng)建有弱耦合的類(lèi)。
2)在類(lèi)的結(jié)構(gòu)設(shè)計(jì)上,每一個(gè)類(lèi)都應(yīng)當(dāng)盡量降低成員的訪(fǎng)問(wèn)權(quán)限。
3)在類(lèi)的設(shè)計(jì)上,只要有可能,一個(gè)類(lèi)應(yīng)當(dāng)設(shè)計(jì)成不變類(lèi)。
4)在對(duì)其他類(lèi)的引用上,一個(gè)對(duì)象對(duì)其對(duì)象的引用應(yīng)當(dāng)降到最低。
5.7 廣義迪米特法則在類(lèi)的設(shè)計(jì)的體現(xiàn)
1)優(yōu)先考慮將一個(gè)類(lèi)設(shè)置成不變類(lèi)
2)盡量降低一個(gè)類(lèi)的訪(fǎng)問(wèn)權(quán)限
3)謹(jǐn)慎使用Serializable
4)盡量降低成員的訪(fǎng)問(wèn)權(quán)限
5)取代C Struct
迪米特法則又叫作最少知識(shí)原則(Least Knowledge Principle或簡(jiǎn)寫(xiě)為L(zhǎng)KP),就是說(shuō)一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象有盡可能少的了解。
5.8 如何實(shí)現(xiàn)迪米特法則
迪米特法則的主要用意是控制信息的過(guò)載,在將其運(yùn)用到系統(tǒng)設(shè)計(jì)中應(yīng)注意以下幾點(diǎn):
1) 在類(lèi)的劃分上,應(yīng)當(dāng)創(chuàng)建有弱耦合的類(lèi)。類(lèi)之間的耦合越弱,就越有利于復(fù)用。
2) 在類(lèi)的結(jié)構(gòu)設(shè)計(jì)上,每一個(gè)類(lèi)都應(yīng)當(dāng)盡量降低成員的訪(fǎng)問(wèn)權(quán)限。一個(gè)類(lèi)不應(yīng)當(dāng)public自己的屬性,而應(yīng)當(dāng)提供取值和賦值的方法讓外界間接訪(fǎng)問(wèn)自己的屬性。
3) 在類(lèi)的設(shè)計(jì)上,只要有可能,一個(gè)類(lèi)應(yīng)當(dāng)設(shè)計(jì)成不變類(lèi)。
4) 在對(duì)其它對(duì)象的引用上,一個(gè)類(lèi)對(duì)其它對(duì)象的引用應(yīng)該降到最低。
六、 接口隔離原則(Interface Segregation Principle, ISP )
6.1 概念
接口隔離原則:使用多個(gè)專(zhuān)門(mén)的接口比使用單一的總接口要好。也就是說(shuō),一個(gè)類(lèi)對(duì)另外一個(gè)類(lèi)的依賴(lài)性應(yīng)當(dāng)是建立在最小的接口上。
這里的"接口"往往有兩種不同的含義:一種是指一個(gè)類(lèi)型所具有的方法特征的集合,僅僅是一種邏輯上的抽象;另外一種是指某種語(yǔ)言具體的"接口"定義,有嚴(yán)格的定義和結(jié)構(gòu)。比如C#語(yǔ)言里面的Interface結(jié)構(gòu)。對(duì)于這兩種不同的含義,ISP的表達(dá)方式以及含義都有所不同。(上面說(shuō)的一個(gè)類(lèi)型,可以理解成一個(gè)類(lèi),我們定義了一個(gè) 類(lèi),也就是定義了一種新的類(lèi)型)
當(dāng)我們把"接口"理解成一個(gè)類(lèi)所提供的所有方法的特征集合的時(shí)候,這就是一種邏輯上的 概念。接口的劃分就直接帶來(lái)類(lèi)型的劃分。這里,我們可以把接口理解成角色,一個(gè)接口就只是代表一個(gè)角色,每個(gè)角色都有它特定的一個(gè)接口,這里的這個(gè)原則可以叫做"角色隔離原則"。
如果把"接口"理解成狹義的特定語(yǔ)言的接口,那么ISP表達(dá)的意思是說(shuō),對(duì)不同的客戶(hù)端,同一個(gè)角色提供寬窄不同的接口,也就是定制服務(wù),個(gè)性化服務(wù)。就是僅僅提供客戶(hù)端需要的行為,客戶(hù)端不需要的行為則隱藏起來(lái)。
應(yīng)當(dāng)為客戶(hù)端提供盡可能小的單獨(dú)的接口,而不要提供大的總接口。
這也是對(duì)軟件實(shí)體之間通信的限制。但它限制的只是通信的寬度,就是說(shuō)通信要盡可能的窄。
遵循迪米特法則和接口隔離原則,會(huì)使一個(gè)軟件系統(tǒng)功能擴(kuò)展時(shí),修改的壓力不會(huì)傳 到別的對(duì)象那里。
6.2 如何實(shí)現(xiàn)接口隔離原則
不應(yīng)該強(qiáng)迫用戶(hù)依賴(lài)于他們不用的方法。
1、利用委托分離接口。
2、利用多繼承分離接口。
七、單一職責(zé)原則(SRP)
單一職責(zé)原則(SRP),就一個(gè)類(lèi)而言,應(yīng)該僅有一個(gè)引起它變化的原因。也就是說(shuō),不要把變化原因各不相同的職責(zé)放在一起,因?yàn)椴煌淖兓瘯?huì)影響到不相干的職責(zé)。再通俗一點(diǎn)地說(shuō)就是,不該你管的事情你不要管,管好自己的事情就可以了,多管閑事害了自己也害了別人。
在軟件設(shè)計(jì)中,如果一個(gè)類(lèi)承擔(dān)的職責(zé)過(guò)多,就等于吧這些職責(zé)耦合在一起,而一個(gè)職責(zé)的變化可能會(huì)削弱和抑制這個(gè)類(lèi)完成其他職責(zé)的能力。這耦合會(huì)導(dǎo)致脆弱的設(shè)計(jì),當(dāng)變化發(fā)生時(shí),設(shè)計(jì)會(huì)遭受到意想不到的破壞。
軟件設(shè)計(jì)真正要做的許多內(nèi)容,就是發(fā)現(xiàn)職責(zé)并把那些職責(zé)相互分離。如果多于一個(gè)的動(dòng)機(jī)去改變一個(gè)類(lèi),那么這個(gè)類(lèi)就具有多余一個(gè)的職責(zé),就應(yīng)該要考慮類(lèi)的職責(zé)分離。
小結(jié)
在我們進(jìn)行面向?qū)ο笙到y(tǒng)的設(shè)計(jì)時(shí),可以不去特意的考慮使用哪些設(shè)計(jì)模式,但是一定要盡量遵守這些設(shè)計(jì)原則。這樣做的話(huà),即使是設(shè)計(jì)經(jīng)驗(yàn)不足,也比較容易設(shè) 計(jì)出易擴(kuò)展的系統(tǒng),并且可能自然的實(shí)現(xiàn)了某些模式。這種情況,恐怕算是很理想的一種設(shè)計(jì)了。
參考資料
2. SOLID (object-oriented design)
it知識(shí)庫(kù):面向?qū)ο笤O(shè)計(jì)的設(shè)計(jì)原則,轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。