面向對象的三個基本特徵

面向對象的三個基本特徵是:封裝、繼承、多態。
o_OOBase.gif
封裝
封裝最好理解了。封裝是面向對象的特徵之一,是對象和類概念的主要特性。
封裝,也就是把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。
繼承
面向對象編程 (OOP) 語言的一個主要功能就是“繼承”。繼承是指這樣一種能力:它可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展。
通過繼承創建的新類稱爲“子類”或“派生類”。
被繼承的類稱爲“基類”、“父類”或“超類”。
繼承的過程,就是從一般到特殊的過程。
要實現繼承,可以通過“繼承”(Inheritance)和“組合”(Composition)來實現。
在某些 OOP 語言中,一個子類可以繼承多個基類。但是一般情況下,一個子類只能有一個基類,要實現多重繼承,可以通過多級繼承來實現。
 
繼承概念的實現方式有三類:實現繼承、接口繼承和可視繼承。
Ø         實現繼承是指使用基類的屬性和方法而無需額外編碼的能力;
Ø         接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實現的能力;
Ø         可視繼承是指子窗體(類)使用基窗體(類)的外觀和實現代碼的能力。
在考慮使用繼承時,有一點需要注意,那就是兩個類之間的關係應該是“屬於”關係。例如,Employee 是一個人,Manager 也是一個人,因此這兩個類都可以繼承 Person 類。但是 Leg 類卻不能繼承 Person 類,因爲腿並不是一個人。
抽象類僅定義將由子類創建的一般屬性和方法,創建抽象類時,請使用關鍵字 Interface 而不是 Class
OO開發範式大致爲:劃分對象→抽象類→將類組織成爲層次化結構(繼承和合成) →用類與實例進行設計和實現幾個階段。
 
多態
多態性(polymorphisn)是允許你將父對象設置成爲和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。
實現多態,有二種方式,覆蓋,重載。
覆蓋,是指子類重新定義父類的虛函數的做法。
重載,是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。
其實,重載的概念並不屬於“面向對象編程”,重載的實現是:編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然後這些同名函數就成了不同的函數(至少對於編譯器來說是這樣的)。如,有兩個同名函數:function func(p:integer):integer;和function func(p:string):integer;。那麼編譯器做過修飾後的函數名稱可能是這樣的:int_func、str_func。對於這兩個函數的調用,在編譯器間就已經確定了,是靜態的(記住:是靜態)。也就是說,它們的地址在編譯期就綁定了(早綁定),因此,重載和多態無關!真正和多態相關的是“覆蓋”。當子類重新定義了父類的虛函數後,父類指針根據賦給它的不同的子類指針,動態(記住:是動態!)的調用屬於子類的該函數,這樣的函數調用在編譯期間是無法確定的(調用的子類的虛函數的地址無法給出)。因此,這樣的函數地址是在運行期綁定的(晚邦定)。結論就是:重載只是一種語言特性,與多態無關,與面向對象也無關!引用一句Bruce Eckel的話:“不要犯傻,如果它不是晚邦定,它就不是多態。”
那麼,多態的作用是什麼呢?我們知道,封裝可以隱藏實現細節,使得代碼模塊化;繼承可以擴展已存在的代碼模塊(類);它們的目的都是爲了——代碼重用。而多態則是爲了實現另一個目的——接口重用!多態的作用,就是爲了類在繼承和派生的時候,保證使用“家譜”中任一類的實例的某一屬性時的正確調用。
 
概念講解
泛化(Generalization
o_Generalization.gif
圖表 1 泛化
在上圖中,空心的三角表示繼承關係(類繼承),在UML的術語中,這種關係被稱爲泛化(Generalization)。Person()是基類,Teacher(教師)Student(學生)Guest(來賓)是子類。
若在邏輯上B是A的“一種”,並且A的所有功能和屬性對B而言都有意義,則允許B繼承A的功能和屬性。
例如,教師是人,Teacher Person的“一種”(a kind of )。那麼類Teacher可以從類Person派生(繼承)。
如果A是基類,B是A的派生類,那麼B將繼承A的數據和函數。
如果類A和類B毫不相關,不可以爲了使B的功能更多些而讓B繼承A的功能和屬性。
若在邏輯上B是A的“一種”(a kind of ),則允許B繼承A的功能和屬性。
 
聚合(組合)
o_aggregationBase.gif
圖表 2 組合
若在邏輯上A是B的“一部分”(a part of),則不允許B從A派生,而是要用A和其它東西組合出B。
例如,眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是頭(Head)的一部分,所以類Head應該由類Eye、Nose、Mouth、Ear組合而成,不是派生(繼承)而成。
 
聚合的類型分爲無、共享(聚合)、複合(組合)三類。
 
聚合(aggregation
 
o_aggregation.gif
圖表 3 共享
上面圖中,有一個菱形(空心)表示聚合(aggregation)(聚合類型爲共享),聚合的意義表示has-a關係。聚合是一種相對鬆散的關係,聚合類B不需要對被聚合的類A負責。
 
組合(composition
o_composition.gif
圖表 4 複合
這幅圖與上面的唯一區別是菱形爲實心的,它代表了一種更爲堅固的關係——組合(composition)(聚合類型爲複合)。組合表示的關係也是has-a,不過在這裏,A的生命期受B控制。即A會隨着B的創建而創建,隨B的消亡而消亡。
 
依賴(Dependency)
o_Dependency.gif
圖表 5 依賴
這裏BA的關係只是一種依賴(Dependency)關係,這種關係表明,如果類A被修改,那麼類B會受到影響。

 

 

UML對很多人來說應該不是一個陌生的概念,這一兩年來,UML被大家越來越多的討論着。本來UML跟我這個主題似乎並不能扯上多大的關係(它是語言無關的,甚至可以說其本身就是一種語言——用於交流的)。我在此談到它有兩個目的:
1. UML是針對面向對象軟件開發的,而C++正是這樣的一種語言
2. UML在設計中被越來越多的使用着,而下一篇雜談準備討論設計模式,如果不瞭解UML,那麼無法進行下去

UML,全稱:Unified Modeling Language,其目的是爲了對軟件密集型的製品進行可視化、詳述、構造和文檔化的圖形語言。UML是依據許多前人的思想總結出的成果,1997年被OMG通過併成爲標準(所以在《設計模式》書中如果你看到與標準不一樣的地方,不要奇怪,那本書是95年的)。關於UML的歷史和更詳細的描述,可以參考《UML 參考手冊》。UML主要由一系列視圖組成,其中包括靜態視圖(Static view),用例視圖(Use case view)活動視圖(Activity view)等,不同的圖用處自然也不一樣,而對開發人員來講(或者說爲我的下一篇來說),更重要的應該是靜態視圖中的類圖(class diagram)和交互視圖(Interaction view)中的順序圖(Sequence diagram),請注意view和diagram的區別。

 類圖
靜態視圖說明了對象的結構,其中最常用的就是類圖,類圖可以幫助我們更直觀的瞭解一個系統的體系結構,有時侯,描述系統快照的對象圖(Object diagram)也是很有用的。在這裏,我們主要介紹類圖,下面的圖就是一個簡單的類圖:

在類圖中,類由矩形框來表示,如上圖中,定義了4個類,分別爲Base、A、B、C,類之間的關係通過各種線條和其他符號來表示,在上圖中,空心的三角表示繼承關係,在UML的術語中,這種關係被稱爲泛化(Generalization),所以上面的類用等價代碼表示爲:
class Base{…};
class A:public Base{…};
class B:public Base{…};
class C:public Base{…};

我們再看下一幅圖:

這幅圖與上幅幾乎沒有什麼區別,唯一的不同就是Base類中增加了成員,一個私有的integer _x(UML術語爲property)和一個公有的fun()的函數(method),是否需要這些類的內部細節UML本身並沒有限制,完全取決於你自己如何使用,UML的用處在於幫助你瞭解系統,所以只要你自己覺得足夠清楚,那麼夠了,不要再複雜了。
接着看第三幅圖:

上面圖中的箭頭表示一種關係,箭頭另一邊有一個菱形(空心)表示聚合(aggregation),聚合的意義表示has-a關係,其等價代碼如下:
class A{…};
class B{ A* theA;…};
聚合是一種相對鬆散的關係,聚合類B不需要對被聚合的類A負責。
下面的圖:

這幅圖與上面的唯一區別是菱形爲實心的,它代表了一種更爲堅固的關係——組合(composition)。組合表示的關係也是has-a,不過在這裏,A的生命期受B控制,通常情況,等價代碼如下:
class A{…};
class B{A theA;…};
即A會隨着B的創建而創建,隨B的消亡而消亡。

下圖:

這裏B與A的關係只是一種依賴關係,這種關係表明,如果類A被修改,那麼類B會受到影響,一個簡單的例子就是:
class A{…};
class B{fun(A params);…};

常用的關係就是我們上面用的這些,通過這些關係和類表示的類圖,我們可以用圖形化的方式描述一個系統的設計部分,當你習慣使用UML後,你會發現,這往往比你告訴同伴某某類從某某類派生,派生類又和某某類具有什麼關係容易的多。

 順序圖:
UML中另外一個常用的圖形就是交互視圖中的順序圖,在以往的過程化語言中,我們通常使用流程圖來描述一個函數(系統)是如何工作的,而在面向對象的系統中,這顯然是不可行的,而順序圖正是來解決這個問題的。
假設有如下的僞代碼:

對於下面的調用:
window wnd;
wnd.drawcircle();
對應的順序圖如下:

 

圖中上方的方塊表示參與的對象,垂直的虛線表示對象的生命線,方框表示激活,其中箭頭表示了一個調用消息(也可以有回送return),如果是異步的消息,則用半箭頭表示,其中draw表示了一個自調用(self call)

至此,UML中最常用的(從開發人員的角度),當然UML的內容遠遠不只這些,這裏的介紹只是一些簡單的概括,並且UML本身也在不斷的發展之中,無論怎樣,我覺得UML會越來越多的深入我們的開發過程中,特別是對下一篇我們要介紹的設計模式而言,類圖是主要的描述工具(到那個時候你會體會到UML描述的優越)。
如果你看過《設計模式》着本書,你會發現與我上面所描述的有一些細微的不同,不要緊張,《設計模式》是GOF95年的作品,那時候UML還沒有形成,而且,其中也明確那是OMT方法(Jim Rumbaugh在通用電氣發表的建模技術——Object Modeling Technique)和Booch方法。如果你覺得UML有些讓你無所適從,也不必緊張,UML本身只是一個輔助工具,它的目的是幫助你描述系統,不是複雜你的工作,如果你的系統很簡單,一句話可以說的很清楚,那麼不要用UML,如果你只想說明類之間的關係,而不是類的接口描述,那麼像第一副圖那樣簡單的描述就很好,總之不要去追求細節,只要能說明問題,那麼你的目的就達到了(甚至你沒有必要完全遵守規範)。

 參考書目
《設計模式——可複用面向對象軟件的基礎》 
作者: Erich Gamma 等 譯者:李英軍 馬曉星 蔡敏 劉建中 機械工業出版社2000

《UML參考手冊》作者: James Rumbaugh Ivar Jacobson Grady Booch
譯者: 姚淑珍 唐髮根 機械工業出版社2001

《UML精粹——標準對象建模語言簡明指南(第2版)》
作者: Martin Fowler&Kendall Scott 譯者: 徐家福 清華大學出版社 2002

《Design Patterns Explained》Alan Shalloway, James R. Trott
Addison-Wesley Pub 2001

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章