面向?qū)ο蟪绦蛟O(shè)計(英語:Object-oriented programming,縮寫:OOP)是種具有對象概念的程序編程典范寂拆,同時也是一種程序開發(fā)的抽象方針奢米。它可能包含數(shù)據(jù)、屬性纠永、代碼與方法恃慧。對象則指的是類的實例。它將對象作為程序的基本單元渺蒿,將程序和數(shù)據(jù)封裝其中痢士,以提高軟件的重用性、靈活性和擴(kuò)展性茂装,對象里的程序可以訪問及經(jīng)常修改對象相關(guān)連的數(shù)據(jù)怠蹂。在面向?qū)ο蟪绦蚓幊汤铮嬎銠C(jī)程序會被設(shè)計成彼此相關(guān)的對象少态。
面向?qū)ο蟪绦蛟O(shè)計可以看作一種在程序中包含各種獨(dú)立而又互相調(diào)用的對象的思想城侧,這與傳統(tǒng)的思想剛好相反:傳統(tǒng)的程序設(shè)計主張將程序看作一系列函數(shù)的集合,或者直接就是一系列對電腦下達(dá)的指令彼妻。面向?qū)ο蟪绦蛟O(shè)計中的每一個對象都應(yīng)該能夠接受數(shù)據(jù)嫌佑、處理數(shù)據(jù)并將數(shù)據(jù)傳達(dá)給其它對象,因此它們都可以被看作一個小型的“機(jī)器”侨歉,即對象屋摇。目前已經(jīng)被證實的是,面向?qū)ο蟪绦蛟O(shè)計推廣了程序的靈活性和可維護(hù)性幽邓,并且在大型項目設(shè)計中廣為應(yīng)用炮温。此外,支持者聲稱面向?qū)ο蟪绦蛟O(shè)計要比以往的做法更加便于學(xué)習(xí)牵舵,因為它能夠讓人們更簡單地設(shè)計并維護(hù)程序柒啤,使得程序更加便于分析倦挂、設(shè)計、理解担巩。反對者在某些領(lǐng)域?qū)Υ擞枰苑裾J(rèn)方援。
當(dāng)我們提到面向?qū)ο蟮臅r候,它不僅指一種程序設(shè)計方法涛癌。它更多意義上是一種程序開發(fā)方式肯骇。在這一方面,我們必須了解更多關(guān)于面向?qū)ο笙到y(tǒng)分析和面向?qū)ο笤O(shè)計(Object Oriented Design祖很,簡稱OOD)方面的知識笛丙。許多流行的編程語言是面向?qū)ο蟮?它們的風(fēng)格就是會透由對象來創(chuàng)出實例。
重要的面向?qū)ο缶幊陶Z言包含Common Lisp假颇、Python胚鸯、C++、Objective-C笨鸡、Smalltalk姜钳、Delphi、Java形耗、Swift哥桥、C#、Perl激涤、Ruby 與 PHP等拟糕。
1.簡介
OOP: Object Oriented Programming,面向?qū)ο蟮某绦蛟O(shè)計。所謂“對象”在顯式支持面向?qū)ο蟮恼Z言中倦踢,一般是指類在內(nèi)存中裝載的實例送滞,具有相關(guān)的成員變量和成員函數(shù)(也稱為:方法)。面向?qū)ο蟮某绦蛟O(shè)計完全不同于傳統(tǒng)的面向過程程序設(shè)計辱挥,它大大地降低了軟件開發(fā)的難度犁嗅,使編程就像搭積木一樣簡單,是當(dāng)今電腦編程的一股勢不可擋的潮流晤碘。
OOP 達(dá)到了軟件工程的三個主要目標(biāo):重用性褂微、靈活性和擴(kuò)展性。為了實現(xiàn)整體運(yùn)算园爷,每個對象都能夠接收信息宠蚂、處理數(shù)據(jù)和向其它對象發(fā)送信息。OOP 主要有以下的概念和組件:
組件 - 數(shù)據(jù)和功能一起在運(yùn)行著的計算機(jī)程序中形成的單元腮介,組件在 OOP 計算機(jī)程序中是模塊和結(jié)構(gòu)化的基礎(chǔ)肥矢。
抽象性 - 程序有能力忽略正在處理中信息的某些方面,即對信息主要方面關(guān)注的能力叠洗。
封裝 - 也叫做信息封裝:確保組件不會以不可預(yù)期的方式改變其它組件的內(nèi)部狀態(tài)甘改;只有在那些提供了內(nèi)部狀態(tài)改變方法的組件中,才可以訪問其內(nèi)部狀態(tài)灭抑。每類組件都提供了一個與其它組件聯(lián)系的接口十艾,并規(guī)定了其它組件進(jìn)行調(diào)用的方法。
多態(tài)性 - 組件的引用和類集會涉及到其它許多不同類型的組件腾节,而且引用組件所產(chǎn)生的結(jié)果依據(jù)實際調(diào)用的類型忘嫉。
繼承性 - 允許在現(xiàn)存的組件基礎(chǔ)上創(chuàng)建子類組件,這統(tǒng)一并增強(qiáng)了多態(tài)性和封裝性案腺。典型地來說就是用類來對組件進(jìn)行分組庆冕,而且還可以定義新類為現(xiàn)存的類的擴(kuò)展,這樣就可以將類組織成樹形或網(wǎng)狀結(jié)構(gòu)劈榨,這體現(xiàn)了動作的通用性访递。
由于抽象性、封裝性同辣、重用性 以及便于使用等方面的原因拷姿,以組件為基礎(chǔ)的編程在腳本語言中已經(jīng)變得特別流行。Python 和 Ruby 是最近才出現(xiàn)的語言旱函,在開發(fā)時完全采用了 OOP 的思想响巢,而流行的 Perl 腳本語言從版本5開始也慢慢地加入了新的面向?qū)ο蟮墓δ芙M件。用組件代替“現(xiàn)實”上的實體成為 JavaScript(ECMAScript) 得以流行的原因棒妨,有論證表明對組件進(jìn)行適當(dāng)?shù)慕M合就可以在英特網(wǎng)上代替 HTML 和 XML 的文檔對象模型(DOM)踪古。
2.OOP思想
面向?qū)ο缶幊碳夹g(shù)的關(guān)鍵性觀念是它將數(shù)據(jù)及對數(shù)據(jù)的操作行為放在一起,作為一個相互依存券腔、不可分割的整體—— 對象灾炭。對于相同類型的對象進(jìn)行分類、抽象后颅眶,得出共同的特征而形成了類蜈出。面向?qū)ο缶幊叹褪嵌x這些類。類是描述相同類型的對象集合涛酗。類定義好之后將作為數(shù)據(jù)類型用于創(chuàng)建類的對象铡原。程序的執(zhí)行表現(xiàn)為一組對象之間的交互通信。對象之間通過公共接口進(jìn)行通信商叹,從而完成系統(tǒng)功能燕刻。類中聲明的public成員組成了對象的對外公共接口箫柳。 簡單來說就是以功能為解決問題的中心配喳。
3.特征
面向?qū)ο蟪绦蚓幊痰亩x是使用“對象”來做設(shè)計柿菩,但并非所有的編程語言都直接支持“面向?qū)ο蟪绦蚓幊獭毕嚓P(guān)技術(shù)與結(jié)構(gòu)逢渔。對于OOP 的準(zhǔn)確定義及其本意存在著不少爭論跋选。通常,OOP被理解為一種將程序分解為封裝數(shù)據(jù)及相關(guān)操作的模塊而進(jìn)行的編程方式族檬。有別于其它編程方式硬鞍,OOP 中的與某數(shù)據(jù)類型相關(guān)的一系列操作都被有機(jī)地封裝到該數(shù)據(jù)類型當(dāng)中,而非散放于其外酷勺,因而OOP 中的數(shù)據(jù)類型不僅有著狀態(tài)本橙,還有著相關(guān)的行為。
OOP理論脆诉,及與之同名的OOP實踐相結(jié)合創(chuàng)造出了新的一個編程架構(gòu)甚亭;OOP思想被廣泛認(rèn)為是非常有用的,以致一套新的編程范型被創(chuàng)造了出來击胜。(其它的編程范型例如函數(shù)式編程或過程式編程專注于程序運(yùn)行的過程亏狰,而邏輯編程專注于引發(fā)程序代碼執(zhí)行的斷言)。對面向模擬系統(tǒng)的語言(如:SIMULA 67)的研究及對高可靠性系統(tǒng)架構(gòu)(如:高性能操作系統(tǒng)和CPU的架構(gòu))的研究最終導(dǎo)致了OOP的誕生偶摔。其中由Deborah J. Armstrong進(jìn)行的長達(dá)40年之久的計算機(jī)著作調(diào)查中暇唾,顯示出了一系列面向?qū)ο蟪绦蛟O(shè)計的基本理論。面向?qū)ο蟪绦蛱卣鞅粭l列如下
3.1分享非面向?qū)ο蟪绦蚯吧碚Z言
面向?qū)ο蟪绦蛟O(shè)計通常共享高級編程語言的低級功能啰挪⌒挪唬可用于建構(gòu)一個程序的基本工具包括:
變量:能存儲一些內(nèi)置類型的信息如整數(shù)與字符,也有些是數(shù)據(jù)結(jié)構(gòu)像是字符串亡呵、串列與散列表等包含內(nèi)置或復(fù)合的變量如指針抽活。
程序:也稱為函數(shù)、方法或例程锰什,是指輸入數(shù)據(jù)產(chǎn)生輸出結(jié)果下硕,現(xiàn)代語言還包含結(jié)構(gòu)化編程結(jié)構(gòu)如程序循環(huán)與條件。
3.2類與對象
支持面向?qū)ο缶幊陶Z言通常利用繼承其他類達(dá)到代碼重用和可擴(kuò)展性的特性汁胆。而類有兩個主要的概念:
類(Class):定義了一件事物的抽象特點(diǎn)梭姓。類的定義包含了數(shù)據(jù)的形式以及對數(shù)據(jù)的操作。
對象:是類的實例嫩码。
其中類別(Class)定義了一件事物的抽象特點(diǎn)誉尖。類的定義包含了數(shù)據(jù)的形式以及對數(shù)據(jù)的操作。舉例來說铸题,“狗”這個類會包含狗的一切基礎(chǔ)特征铡恕,即所有“狗”都共有的特征或行為,例如它的孕育丢间、毛皮顏色和吠叫的能力探熔。類可以為程序提供模版和結(jié)構(gòu)。一個類的方法和屬性被稱為“成員”烘挫。 我們來看一段偽代碼:
類 狗
開始
公有成員:
吠叫():
私有成員:
毛皮顏色:
孕育:
結(jié)束
在這串代碼中诀艰,我們聲明了一個類,這個類具有一些狗的基本特征。關(guān)于公有成員和私有成員其垄,請參見下面的繼承性一節(jié)苛蒲。
對象(Object)是類的實例。對象有時會對應(yīng)到現(xiàn)實世界中的事物捉捅,舉例來說撤防,一個圖形程序可能有圓形虽风、矩形與畫面等對象棒口,一個在線購物系統(tǒng)可能有購物車、顧客與產(chǎn)品等類辜膝。有時對象會表示更抽象的實體无牵,比如一個被打開的文件或是一個提供美國慣用量測轉(zhuǎn)換的服務(wù)。每個對象就是一個特定類的實例(例如厂抖,名稱是“瑪麗”的對象可能是類雇員的一個實例)茎毁。程序在面向?qū)ο缶幊坍?dāng)中被視為方法,變量被視為成員或?qū)傩猿栏ā@缙咧肮贰边@個類列舉狗的特點(diǎn),從而使這個類定義了世界上所有的狗墙懂。而萊絲這個對象則是一條具體的狗橡卤,它的屬性也是具體的。狗有皮毛顏色损搬,而萊絲的皮毛顏色是棕白色的碧库。因此,萊絲就是狗這個類的一個實例巧勤。一個具體對象屬性的值被稱作它的“狀態(tài)”嵌灰。(系統(tǒng)給對象分配內(nèi)存空間,而不會給類分配內(nèi)存空間颅悉。這很好理解沽瞭,類是抽象的系統(tǒng)不可能給抽象的東西分配空間,而對象則是具體的剩瓶。)
假設(shè)我們已經(jīng)在上面定義了狗這個類驹溃,我們就可以用這個類來定義對象:
定義萊絲是狗
萊絲.毛皮顏色:棕白色
萊絲.吠叫()
我們無法讓狗這個類去吠叫,但是我們可以讓對象“萊絲”去吠叫儒搭,正如狗可以吠叫吠架,但沒有具體的狗就無法吠叫。
類和對象就好比是“實型”和“1.23”搂鲫,“實型”是一種數(shù)據(jù)的類型傍药,而“1.23”是一個真正的“實數(shù)”(即對象)。所有的“實數(shù)”都具有“實型”所描訴的特征,如“實數(shù)的大小”拐辽,系統(tǒng)則分配內(nèi)存給“實數(shù)”存儲具體的數(shù)值拣挪。
3.3動態(tài)配置與消息傳遞機(jī)制
定義上動態(tài)配置是指方法會隨著實例動態(tài)的改變。而消息傳遞機(jī)制(Message Passing)是指一個對象通過接受消息俱诸、處理消息菠劝、傳出消息或使用其他類的方法來實現(xiàn)一定功能。如:萊絲可以通過吠叫引起人的注意睁搭,從而導(dǎo)致一系列的事發(fā)生赶诊。
3.4封裝性
具備封裝性(Encapsulation)的面向?qū)ο蟪绦蛟O(shè)計隱藏了某一方法的具體運(yùn)行步驟,取而代之的是通過消息傳遞機(jī)制發(fā)送消息給它园骆。封裝是通過限制只有特定類的對象可以訪問這一特定類的成員舔痪,而它們通常利用接口實現(xiàn)消息的傳入傳出。舉個例子锌唾,接口能確保幼犬這一特征只能被賦予狗這一類锄码。通常來說,成員會依它們的訪問權(quán)限被分為3種:公有成員晌涕、私有成員以及保護(hù)成員滋捶。有些語言更進(jìn)一步:Java可以限制同一包內(nèi)不同類的訪問;C#和VB.NET保留了為類的成員聚集準(zhǔn)備的關(guān)鍵字:internal(C#)和Friend(VB.NET)余黎;Eiffel語言則可以讓用戶指定哪個類可以訪問所有成員重窟。
具備封裝性(Encapsulation)的面向?qū)ο蟪绦蛟O(shè)計隱藏了某一方法的具體執(zhí)行步驟,取而代之的是通過消息傳遞機(jī)制傳送消息給它驯耻。因此亲族,舉例來說,“狗”這個類有“吠叫()”的方法可缚,這一方法定義了狗具體該通過什么方法吠叫霎迫。但是,萊絲的朋友并不知道它到底是如何吠叫的帘靡。
從實例來看:
/* 一個面向過程的程序會這樣寫: */
定義萊絲
萊絲.設(shè)置音調(diào)(5)
萊絲.吸氣()
萊絲.吐氣()
/* 而當(dāng)狗的吠叫被封裝到類中知给,任何人都可以簡單地使用: */
定義萊絲是狗
萊絲.吠叫()
3.5繼承
繼承性(Inheritance)是指,在某種情況下描姚,一個類會有“子類”涩赢。子類比原本的類(稱為父類)要更加具體化。例如轩勘,“狗”這個類可能會有它的子類“牧羊犬”和“吉娃娃犬”筒扒。在這種情況下,“萊絲”可能就是牧羊犬的一個實例绊寻。子類會繼承父類的屬性和行為花墩,并且也可包含它們自己的悬秉。我們假設(shè)“狗”這個類有一個方法(行為)叫做“吠叫()”和一個屬性叫做“毛皮顏色”。它的子類(前例中的牧羊犬和吉娃娃犬)會繼承這些成員冰蘑。這意味著程序員只需要將相同的代碼寫一次和泌。
在偽代碼中我們可以這樣寫:
類牧羊犬:繼承狗
定義萊絲是牧羊犬
萊絲.吠叫() /* 注意這里調(diào)用的是狗這個類的吠叫方法。*/
回到前面的例子祠肥,“牧羊犬”這個類可以繼承“毛皮顏色”這個屬性武氓,并指定其為棕白色。而“吉娃娃犬”則可以繼承“吠叫()”這個方法仇箱,并指定它的音調(diào)高于平常县恕。子類也可以加入新的成員,例如工碾,“吉娃娃犬”這個類可以加入一個方法叫做“顫抖()”弱睦。設(shè)若用“牧羊犬”這個類定義了一個實例“萊絲”百姓,那么萊絲就不會顫抖渊额,因為這個方法是屬于吉娃娃犬的,而非牧羊犬垒拢。事實上旬迹,我們可以把繼承理解為“是”或“屬于”。萊絲“是”牧羊犬求类,牧羊犬“屬于”狗類奔垦。因此,萊絲既得到了牧羊犬的屬性尸疆,又繼承了狗的屬性椿猎。 我們來看偽代碼:
類吉娃娃犬:繼承狗
開始
公有成員:
顫抖()
結(jié)束
類牧羊犬:繼承狗
定義萊絲是牧羊犬
萊絲.顫抖() /* 錯誤:顫抖是吉娃娃犬的成員方法。 */
當(dāng)一個類從多個父類繼承時寿弱,我們稱之為“多重繼承”犯眠。如一只狗既是吉娃娃犬又是牧羊犬(雖然事實上并不合邏輯)。多重繼承并不總是被支持的症革,因為它很難理解筐咧,又很難被好好使用。
3.6多態(tài)
多態(tài)(Polymorphism)是指由繼承而產(chǎn)生的相關(guān)的不同的類噪矛,其對象對同一消息會做出不同的響應(yīng)量蕊。例如,狗和雞都有“叫()”這一方法艇挨,但是調(diào)用狗的“叫()”残炮,狗會吠叫;調(diào)用雞的“叫()”缩滨,雞則會啼叫势就。 我們將它體現(xiàn)在偽代碼上:
類狗
開始
公有成員:
叫()
開始
吠叫()
結(jié)束
結(jié)束
類雞
開始
公有成員:
叫()
開始
啼叫()
結(jié)束
結(jié)束
定義萊絲是狗
定義魯斯特是雞
萊絲.叫()
魯斯特.叫()
這樣辞居,雖然同樣是做出叫這一種行為,但萊絲和魯斯特具體做出的表現(xiàn)方式將大不相同蛋勺。多態(tài)性的概念可以用在運(yùn)算符重載上瓦灶,本文不再贅述。
3.7抽象性
抽象(Abstraction)是簡化復(fù)雜的現(xiàn)實問題的途徑抱完,它可以為具體問題找到最恰當(dāng)?shù)念惗x贼陶,并且可以在最恰當(dāng)?shù)睦^承級別解釋問題。舉例說明巧娱,萊絲在大多數(shù)時候都被當(dāng)作一條狗碉怔,但是如果想要讓它做牧羊犬做的事,你完全可以調(diào)用牧羊犬的方法禁添。如果狗這個類還有動物的父類撮胧,那么你完全可以視萊絲為一個動物。
4.優(yōu)缺點(diǎn)
OOP 的優(yōu)點(diǎn):使人們的編程與實際的世界更加接近老翘,所有的對象被賦予屬性和方法芹啥,結(jié)果編程就更加富有人性化。
OOP 的缺點(diǎn):就 C++而言铺峭,由于面向更高的邏輯抽象層墓怀,使得 C++ 在實現(xiàn)的時候,不得不做出性能上面的犧牲卫键,有時候甚至是致命的 傀履。
5.面向?qū)ο缶幊陶Z言
支持部分或絕大部分面向?qū)ο筇匦缘恼Z言即可稱為基于對象的或面向?qū)ο蟮恼Z言。Simula (1967)被視為第一個具有面向?qū)ο筇匦缘恼Z言莉炉。早期钓账,完全面向?qū)ο蟮恼Z言主要包括Smalltalk等語言,目前較為流行的語言中有Java絮宁、C#梆暮、Eiffel等。隨著軟件工業(yè)的發(fā)展羞福,比較早的面向過程的語言在近些年的發(fā)展中也紛紛吸收了許多面向?qū)ο蟮母拍钐杼悖热?strong>C→C++,C→Objective-C治专,BASIC→Visual Basic→Visual Basic .NET卖陵,Pascal→Object Pascal,Ada→Ada95张峰±崮瑁“純粹”的面向?qū)ο笳Z言, 因為所有的東西都是由對象所組成,例如: Eiffel, Emerald, JADE, Obix, Ruby, Scala, Smalltalk, Self.
參考資料:
1.OOP
2.面向?qū)ο蟪绦蛟O(shè)計