面向?qū)ο蟮脑O(shè)計(jì)思想

面向?qū)ο笫且环N設(shè)計(jì)的思想,與具體實(shí)現(xiàn)的語言工具無關(guān)。能讓軟件架構(gòu)更符合人的思維模式,更為清晰明了,更易于理解與維護(hù)。這是大型軟件必然的選擇。

面向?qū)ο蟮某绦蛟O(shè)計(jì)語言必須有描述對象及其相互之間關(guān)系的語言成分。這些程序設(shè)計(jì)語言可以歸納為以下幾類:系統(tǒng)中一切事物皆為對象;對象是屬性及其操作的封裝體;對象可按其性質(zhì)劃分為類,對象成為類的實(shí)例;實(shí)例關(guān)系和繼承關(guān)系是對象之間的靜態(tài)關(guān)系;消息傳遞是對象之間動態(tài)聯(lián)系的唯一形式,也是計(jì)算的唯一形式;方法是消息的序列。

面向?qū)ο缶幊獭狾bject Oriented Programming,簡稱OOP,是一種程序設(shè)計(jì)思想。OOP把對象作為程序的基本單元,一個對象包含了數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)。

面向過程的程序設(shè)計(jì)把計(jì)算機(jī)程序視為一系列的命令集合,即一組函數(shù)的順序執(zhí)行。為了簡化程序設(shè)計(jì),面向過程把函數(shù)繼續(xù)切分為子函數(shù),即把大塊函數(shù)通過切割成小塊函數(shù)來降低系統(tǒng)的復(fù)雜度。
而面向?qū)ο蟮某绦蛟O(shè)計(jì)把計(jì)算機(jī)程序視為一組對象的集合,而每個對象都可以接收其他對象發(fā)過來的消息,并處理這些消息,計(jì)算機(jī)程序的執(zhí)行就是一系列消息在各個對象之間傳遞。

萬物皆為對象,對象是對現(xiàn)實(shí)事物的一種抽象,通過程序來實(shí)現(xiàn)對事物的描述。面向?qū)ο缶幊痰娜筇卣鳎悍庋b、繼承和多態(tài)。

I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages (so messaging came at the very beginning -- it took a while to see how to do messaging in a programming language efficiently enough to be useful).
...
OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP.

簡單解釋一下上面的這幾句話的大概意思:OOP應(yīng)該體現(xiàn)一種網(wǎng)狀結(jié)構(gòu),這個結(jié)構(gòu)上的每個節(jié)點(diǎn)“Object”只能通過“消息”和其他節(jié)點(diǎn)通訊。每個節(jié)點(diǎn)會有內(nèi)部隱藏的狀態(tài),狀態(tài)不可以被直接修改,而應(yīng)該通過消息傳遞的方式來間接的修改。

我的評注: 在面向過程的方法中,我們頭腦中首先出現(xiàn)的是類似流程圖的的東西,而采用OOP我們頭腦中首先出現(xiàn)的是類似對象關(guān)系圖的東西。

面向?qū)ο蟮谋举|(zhì)和面向?qū)ο蟮某绦蛘Z言:

面向?qū)ο蟮谋举|(zhì)是什么?答案是抽象。從面對的問題域抽象出解決問題所需的對象是面向?qū)ο蠓椒ǖ暮诵乃枷搿D芊袂‘?dāng)抽象出足夠的對象類型,特別是抽象出潛在的對象是決定軟件設(shè)計(jì)好壞的關(guān)鍵。如果從更寬泛的角度講,對我們所面對的復(fù)雜問題進(jìn)行抽象,抓住本質(zhì),得出高度精煉的邏輯模型,對問題的求解具有重要的意義。從這個角度來說,抽象并不僅僅局限于對象的抽象,也包括流程和更高層的系統(tǒng)結(jié)構(gòu)。

而封裝、繼承、多態(tài)是面向?qū)ο蟮奶卣?,這是采用面向?qū)ο蟮姆椒ㄋ軌蜻_(dá)到的效果。注意區(qū)分特點(diǎn)和本質(zhì)。

類的定義類具有屬性和行為,它是將數(shù)據(jù)和與數(shù)據(jù)相關(guān)的操作封裝在一起的集合體,類定義中的成員即成員變量和成員函數(shù)。

   面向?qū)ο笫腔谌f物皆對象這個哲學(xué)觀點(diǎn). 把一個對象抽象成類,具體上就是把一個對象的靜態(tài)特征和動態(tài)特征抽象成屬性和方法,也就是把一類事物的算法和數(shù)據(jù)結(jié)構(gòu)封裝在一個類之中,程序就是多個對象和互相之間的通信組成的.

   面向?qū)ο缶哂蟹庋b性,繼承性,多態(tài)性.封裝隱蔽了對象內(nèi)部不需要暴露的細(xì)節(jié),使得內(nèi)部細(xì)節(jié)的變動跟外界脫離,只依靠接口進(jìn)行通信.封裝性降低了編程的復(fù)雜性. 通過繼承,使得新建一個類變得容易,一個類從派生類那里獲得其非私有的方法和公用屬性的繁瑣工作交給了編譯器. 而 繼承和實(shí)現(xiàn)接口和運(yùn)行時的類型綁定機(jī)制 所產(chǎn)生的多態(tài),使得不同的類所產(chǎn)生的對象能夠?qū)ο嗤南⒆鞒霾煌姆磻?yīng),極大地提高了代碼的通用性. 

   總之,面向?qū)ο蟮奶匦蕴岣吡舜笮统绦虻闹赜眯院涂删S護(hù)性。

更多思考

分離變化

分離變化是貫穿在整個面向?qū)ο笤O(shè)計(jì)中的核心思想,具有非常重要的指導(dǎo)意義。

封裝內(nèi)部變化,擴(kuò)展外部變化。封裝內(nèi)部變化自不必說,通過繼承和多態(tài)擴(kuò)展外部變化,這就是面向?qū)ο蟮娜筇卣靼?。面向?qū)ο笏枷牒艽蟪潭壬暇褪敲嫦蜃兓乃枷耄迅鞣N各樣的變化封裝為對象,合理的分配這些對象,當(dāng)然這里的合理是有技巧的。

軟件設(shè)計(jì)應(yīng)該基于不變的基石,分離變化的意義就在于此,幫助建立穩(wěn)定模型。

分析變化需要一定的經(jīng)驗(yàn),if-else語句暗示著變化,擴(kuò)展的業(yè)務(wù)暗示著變化…

轉(zhuǎn)移變化需要一定的技巧,封裝、依賴倒置、增加間接層…

繼承與組合

面向?qū)ο笙到y(tǒng)功能復(fù)用的兩大技術(shù)包括類繼承和對象組合,二者選擇有一句“格言”:優(yōu)先使用對象組合,而不是類繼承(Favor object composition over class inheritance)。這句話我很贊同,但我還想強(qiáng)調(diào)的是,二者的使用場景不同,并不能相互替換:類繼承強(qiáng)調(diào)的是抽象復(fù)用(屬于同一類)而對象組合強(qiáng)調(diào)的是實(shí)現(xiàn)復(fù)用(借用一下其他對象的行為實(shí)現(xiàn))。而在實(shí)際項(xiàng)目中類繼承常常被濫用于實(shí)現(xiàn)復(fù)用,我們應(yīng)該消除此類過度繼承,這才是我們強(qiáng)調(diào)組合優(yōu)于繼承的最主要原因。

絕大部分設(shè)計(jì)模式都基于繼承或者組合實(shí)現(xiàn),甚至有的設(shè)計(jì)模式比如適配器模式使用繼承和組合都是正確的,分別為類適配器和對象適配器模式。而有的設(shè)計(jì)模式比如橋梁模式則是典型的同時使用了繼承和組合,分別負(fù)責(zé)抽象部分和實(shí)現(xiàn)部分的復(fù)用。

抽象與擴(kuò)展

有抽象的地方就有擴(kuò)展,需要擴(kuò)展的地方就需要抽象。
面向?qū)ο笏枷朐谶@點(diǎn)上的支持非常好,假如現(xiàn)在有一個Object,抽象一下就變成了:

// interface or abstract class
public interface IAbstract {
}
public class ObjectA implements IAbstract {
}
public class ObjectB implements IAbstract {
}

這種結(jié)構(gòu)我這邊給取個名字”O(jiān)U結(jié)構(gòu)”,OU的意思是ObjectUnit,面向?qū)ο髥卧囊馑?,操作起來非常具體,后續(xù)文章中會大量用到這種方法。各個設(shè)計(jì)模式充斥著大量的這類代碼,希望大家看到這樣的一組結(jié)構(gòu),可以看成是“一個”類,一個包含著博大精深的面向?qū)ο笏枷氲念?,這會大大簡化你對設(shè)計(jì)模式的理解。

從具體的接口與實(shí)現(xiàn)角度分析,我們應(yīng)該面向接口編程而不是面向?qū)崿F(xiàn)編程。接口本身是抽象,實(shí)現(xiàn)就是遵循這種抽象規(guī)則的具體擴(kuò)展。在面向?qū)ο笾?,inteface或者abstract class巧妙的把抽象和擴(kuò)展連接起來了,這很關(guān)鍵。

對象關(guān)系

在java以及其他的面向?qū)ο笤O(shè)計(jì)模式中,類與類之間主要有6種關(guān)系,他們分別是:依賴、關(guān)聯(lián)、聚合、組合、繼承、實(shí)現(xiàn)。他們的耦合度依次增強(qiáng)。

當(dāng)我們在討論耦合的時候,很多時候是在討論對象的關(guān)系。對象關(guān)系主要包括以下五種:實(shí)現(xiàn)、繼承、組合、聚合、關(guān)聯(lián)、依賴。這些關(guān)系的耦合度依次遞減(是不是體現(xiàn)了“組合優(yōu)于繼承”的思想)。
應(yīng)該準(zhǔn)確的使用這些對象關(guān)系,而避免使用不合適的對象關(guān)系。

其實(shí)很多書中,面向?qū)ο蟮年P(guān)系不是六種,而是4種:依賴、關(guān)聯(lián)、繼承、實(shí)現(xiàn)。聚合和組合是比較特殊的關(guān)聯(lián)關(guān)系。分為六種的其實(shí)就是把關(guān)聯(lián)根據(jù)耦合度從小到大又分為了關(guān)聯(lián)、聚合、組合。

聚合:通過setter方法從傳入其他類的參數(shù)進(jìn)來 組合:在構(gòu)造方法中實(shí)例化其他類。

Public class People{
    Soul soul;
    Body body; 
    //組合關(guān)系中的成員變量一般會在構(gòu)造方法實(shí)例化
     Public People(){ 
        soul = news Soul();
        body = new Body();
    }

聚合是一種“整體-部分”的關(guān)系,組合也是一種“整體-部分”的關(guān)系,區(qū)別在于聚合中的整體和部分有著獨(dú)立的生命周期,而組合中的整體和部分的是生命周期應(yīng)該時一樣的,java中實(shí)現(xiàn)貌似無法有體現(xiàn)這一點(diǎn),而C++中能夠體現(xiàn)。個人認(rèn)為這些區(qū)別更多的體現(xiàn)在語義上,而非實(shí)現(xiàn)上。同時認(rèn)為上面聚合例子不能體現(xiàn)整體與部分的關(guān)系。

對象之間的關(guān)系:依賴(需要某種服務(wù)),關(guān)聯(lián)(對象間有某種對應(yīng)關(guān)系),聚合,組合,繼承...

依賴:對象之間最弱的一種關(guān)聯(lián)方式,是臨時性的關(guān)聯(lián)。代碼中一般指由局部變量、函數(shù)參數(shù)、返回值建立的對于其他對象的調(diào)用關(guān)系。 依賴一般情況下是以下幾種情況之一:
a、ClassA中某個方法的參數(shù)類型是ClassB; 這種情況成為耦合;
b、ClassA中某個方法的參數(shù)類型是ClassB的一個屬性; 這種情況成為緊耦合;
c、ClassA中某個方法的實(shí)現(xiàn)實(shí)例化ClassB;
d、ClassA中某個方法的返回值的類型是ClassB;如果出現(xiàn)了上述四種情況之一,兩個類很有可能就是“依賴”關(guān)系。

大多數(shù)情況下,依賴關(guān)系體現(xiàn)在某個類的方法使用另一個類的對象作為參數(shù)。

依賴關(guān)系(Dependency):是類與類之間的連接,依賴總是單向的。依賴關(guān)系代表一個類依賴于另一個類的定義。下面的例子中class A 依賴與class B、C、D。

public class A{ 
    public B getB(C c, D d){ 
    E e = new E();          
    B b = new B(c, d, e);
    }     
} 

關(guān)聯(lián):
對象之間一種引用關(guān)系,比如客戶類與訂單類之間的關(guān)系。這種關(guān)系通常使用類的屬性表達(dá)。 關(guān)聯(lián)關(guān)系所涉及的兩個類是處于同一層次上的,而在聚合關(guān)系中,兩個類處在不平等的層次上的,一個代表整體,一個代表部分。(關(guān)聯(lián)與聚合僅僅從語法上是區(qū)分不開的,需要察所涉及的類之間的邏輯關(guān)系。)關(guān)聯(lián)是一種結(jié)構(gòu)關(guān)系,說明一個事物的對象與另一個事物的對象相聯(lián)系。給定一個連接兩各類的關(guān)聯(lián),可以從一個類的對象導(dǎo)航到另一個類的對象。關(guān)聯(lián)類通過一條虛線與關(guān)聯(lián)連接關(guān)聯(lián)可以有方向,即導(dǎo)航。一般不作說明的時候,導(dǎo)航是雙向的,不需要在線上標(biāo)出箭頭。大部分情況下導(dǎo)航是單向的,可以加一個箭頭表示。關(guān)聯(lián)在代碼中一般表示為屬性(成員變量),例如下面例子中 class A與B關(guān)聯(lián)public class A{ private B b; } 如果B也關(guān)聯(lián)到A,那么它們就是雙向的關(guān)聯(lián)。public class B{ private A a; }

關(guān)聯(lián)和依賴的區(qū)別:
從類之間關(guān)系的強(qiáng)弱程度來分,關(guān)聯(lián)表示類之間的很強(qiáng)的關(guān)系;依賴表示類之間的較弱的關(guān)系;
從類之間關(guān)系的時間角度來分,
關(guān)聯(lián)表示類之間的“持久”關(guān)系,這種關(guān)系一般表示一種重要的業(yè)務(wù)之間的關(guān)系,需要保存的,或者說需要“持久化”的,或者說需要保存到數(shù)據(jù)庫中的。比如學(xué)生管理系統(tǒng)中的Student類和Class(班級)類,一個Student對象屬于哪個Class是一個重要的業(yè)務(wù)關(guān)系,如果這種關(guān)系不保存,系統(tǒng)就無法管理。另外,依賴表示類之間的是一種“臨時、短暫”關(guān)系,這種關(guān)系是不需要保存的,比如Student類和StuEditScreen(學(xué)生登錄界面)類之間就是一種依賴關(guān)系,StuEditScreen類依賴Student類,依賴Student對象的信息來顯示編輯學(xué)生信息。

聚合(關(guān)聯(lián)關(guān)系的一種):表示has-a的關(guān)系,是一種不穩(wěn)定的包含關(guān)系。聚合類不必對被聚合類負(fù)責(zé)。使用集合屬性表達(dá)聚合關(guān)系,當(dāng)對象A被加入到對象B中,成為對象B的組成部分時,對象B和對象A之間為聚集關(guān)系。聚合是關(guān)聯(lián)關(guān)系的一種,是較強(qiáng)的關(guān)聯(lián)關(guān)系,強(qiáng)調(diào)的是整體與部分之間的關(guān)系與關(guān)聯(lián)關(guān)系一樣,聚合關(guān)系也是通過實(shí)例變量來實(shí)現(xiàn)這樣關(guān)系的。關(guān)聯(lián)關(guān)系和聚合關(guān)系來語法上是沒辦法區(qū)分的,從語義上才能更好的區(qū)分兩者的區(qū)別。聚合關(guān)系(Aggregation):是關(guān)聯(lián)關(guān)系的一種,是強(qiáng)的關(guān)聯(lián)關(guān)系。聚合是整體與個體之間的關(guān)系。如汽車類與引摯類,輪胎類之間的關(guān)系就是整體與個體的關(guān)系。
與關(guān)聯(lián)關(guān)系一樣,聚合關(guān)系也是通過實(shí)例變量來實(shí)現(xiàn)的。空心菱形關(guān)聯(lián)和聚集的區(qū)別:
(1)關(guān)聯(lián)關(guān)系所涉及的兩個對象是處在同一個層次上的。比如人和自行車就是一種關(guān)聯(lián)關(guān)系,而不是聚合關(guān)系,因?yàn)槿瞬皇怯勺孕熊嚱M成的。
聚合關(guān)系涉及的兩個對象處于不平等的層次上,一個代表整體,一個代表部分。比如電腦和它的顯示器、鍵盤、主板以及內(nèi)存就是聚集關(guān)系,因?yàn)橹靼迨请娔X的組成部分。
(2)對于具有聚集關(guān)系(尤其是強(qiáng)聚集關(guān)系)的兩個對象,整體對象會制約它的組成對象的生命周期。部分類的對象不能單獨(dú)存在,它的生命周期依賴于整體類的對象的生命周期,當(dāng)整體消失,部分也就隨之消失。比如張三的電腦被偷了,那么電腦的所有組件也不存在了,除非張三事先把一些電腦的組件(比如硬盤和內(nèi)存)拆了下來。

四、聚合關(guān)系(Aggregation)

聚合關(guān)系(Aggregation):表示的是整體和部分的關(guān)系,整體與部分 可以分開.

? 聚合關(guān)系(Aggregation) 表示一個整體與部分的關(guān)系。通常在定義一個整體類后,再去分析這個整體類的組成結(jié)構(gòu),從而找出一些成員類,該整體類和成員類之間就形成了聚合 關(guān)系。
? 在聚合關(guān)系中,成員類是整體類的一部分,即成員對象是整體對象的一部分,但是成員對象可以脫離整體對象獨(dú)立存在。在UML中,聚合關(guān)系用帶空心菱形的直線表示。

如:電話機(jī)包括一個話筒 電腦包括鍵盤、顯示器,一臺電腦可以和多個鍵盤、多個顯示器搭配,確定鍵盤和顯示器是可以和主機(jī)分開的,主機(jī)可以選擇其他的鍵盤、顯示器組成電腦;

五、組合關(guān)系(Composition)

組合關(guān)系(Composition):也是整體與部分的關(guān)系,但是整體與部分不可以分開.

? 組合關(guān)系(Composition)也表示類之間整體和部分的關(guān)系,但是組合關(guān)系中部分和整體具有統(tǒng)一的生存期。一旦整體對象不存在,部分對象也將不存在,部分對象與整體對象之 間具有同生共死的關(guān)系。
? 在組合關(guān)系中,成員類是整體類的一部分,而且整體類可以控制成員類的生命周期,即成員類的存在依賴于整體類。在UML中,組合關(guān)系用帶實(shí)心菱形的直線表示。

如:公司和部門,部門是部分,公司是整體,公司A的財(cái)務(wù)部不可能和公司B的財(cái)務(wù)部對換,就是說,公司A不能和自己的財(cái)務(wù)部分開; 人與人的心臟.

組合:表示contains-a的關(guān)系,是一種強(qiáng)烈的包含關(guān)系。組合類負(fù)責(zé)被組合類的生命周期。也使用集合屬性表達(dá)聚合關(guān)系 ,是關(guān)聯(lián)關(guān)系的一種,是比聚合關(guān)系強(qiáng)的關(guān)系。它要求普通的聚合關(guān)系中代表的對象負(fù)責(zé)代表部分的對象的生命周期,合成關(guān)系是不能共享的。
代表整體的對象需要負(fù)責(zé)保持對象的存活,在一些情況下負(fù)責(zé)將代表部分的對象湮滅掉。代表整體的對象可以將代表部分的對象傳遞給另一個對象,由后者負(fù)責(zé)此對象的生命周期。換言之,代表部分的對象在每一個時刻只能與一個對象發(fā)生合成關(guān)系,由后者排它的負(fù)責(zé)其生命周期,(聚合和組合的明顯的區(qū)別是:如果類B含有A類對象的指針,那算聚合(生存周期不一樣),如果類B含有A類對象的變量為屬性,那么就必為組合(生存周期必須相同)),實(shí)心菱形

繼承:表示is-a的關(guān)系,是對象之間耦合度最大的一種關(guān)系,子類繼承父類的所有細(xì)節(jié)。直接使用語言中的繼承表達(dá)。 類圖中繼承的表示方法是從子類拉出一條閉合的、單鍵頭(或三角形)的實(shí)線指向基類
從使用的頻率來看,關(guān)聯(lián)(包括聚合和組合)關(guān)系是使用最為廣泛的,其次是依賴和繼承
設(shè)計(jì)類之間的關(guān)系是遵循的原則:
首先判斷類之間是否是一種“關(guān)聯(lián)”關(guān)系,
若不是再判斷是否是“依賴關(guān)系”,
一般情況下若不是關(guān)聯(lián),就是依賴關(guān)系

聚合關(guān)系建議使用部門與員工來舉例,組合關(guān)系建議使用公司與部門來舉例。部門由員工組成,部門解散員工照樣生活。公司由部門組成,公司破產(chǎn)倒閉,部門則不復(fù)存在。

我覺得這個里面的聚合,是不是說錯了?聚合難道不是部門和員工之間的關(guān)系么?應(yīng)該強(qiáng)調(diào)的是同類群體和個體的關(guān)系,而不是汽車和汽車零件的關(guān)系吧,汽車和汽車零件應(yīng)該是組合關(guān)系吧?兩者不是一個類型。汽車離不開汽車零件,但是部門和員工之間的關(guān)系可不一樣啊,部門去掉幾個員工也沒啥關(guān)系啊。
比較同意你的看法。 聚合是has-a關(guān)系,集體和個體關(guān)系。組合是contain-a關(guān)系,整體與部分的關(guān)系,關(guān)聯(lián)關(guān)系比聚合強(qiáng)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容