第二課 重要范式(3)
2.3 對象范式——民主制社會的編程法則
民為貴,社稷次之,君為輕 ——《孟子·盡心下》
關鍵詞:編程范式,OOP,面向對象,過程式編程
摘要: OOP簡談
?提問
OOP是一種特殊的命令式嗎?
OOP的基本思想是什麼?
OOP到底好在哪裡?
OOP將要一統天下嗎?
過程式編程與OOP在設計理念上有什麼差異?
:講解
短憩之後,引號迫不及待地問:“面向對象的范式應該是一種特殊的命令式吧?”
“面向對象?”冒號咕哝著,“姑且稱之為OO或對象式吧,既不標新立異,也不以訛傳訛。在回答你的問題之前,請先回答我的:什麼是OOP?”
引號應答如流:“OOP(Object-Oriented programming)是一種計算機編程模式,它以對象作為問題空間的基本元素,利用對象和對象間的相互作用來設計程序。所謂對象,是實際問題中實體的抽象,具有一定的屬性和功能。OOP的三個基本特性是:封裝性、繼承性和多態性。所謂封裝性就是——”
冒號作了個暫停的手勢:“OOP的的基本特性相信大家早就耳熟能詳了,那麼根據你剛才的定義,能否得出OOP一定是命令式的結論?”
引號歪頭想了一陣,答道:“從定義上好像並不能得出,難道C++、Java、C#不是命令式的嗎?”
冒號回答:“當然是,但這不妨礙Clos成為OO版的Lisp,而Prolog也有不少融入OO特征的擴充,如Visual Prolog、Logtalk等。OOP雖然是在命令式的基礎上發展起來的,但其核心思想可泛化為:以數據為中心組織邏輯,將系統視為相互作用的對象集合,並利用繼承與多態來增強重用性。這種思想也能應用到函數式和邏輯式中,只不過對象的方法從命令式中的過程分別換成函數式中的函數和邏輯式中的斷言罷了。大致說來,命令式、函數式和邏輯式互相平行,而OOP與它們正交。”
問號提問:“OOP已經成為一種潮流,上堂課列舉的十二種流行語言中只有C不是OO的,這是否意味著OOP將要一統天下?”
“嚴格說來,VB(VB.NET除外)和JavaScript也不是OO的,只是基於對象的(Object Based)[1]。” 冒號糾正道,“至於OOP是否會一統天下,答案是否定的。首先,與能獨當一面的三類最基本的范式不同,純粹的OOP是不存在的[2],必須結合其他范式;其次,世上沒有包治百病的萬靈丹方,OOP也不例外。用軟件業的行話來說:沒有銀彈(No silver bullet)[3]。OOP最適用於大型復雜的、交互式的、尤其是與現實世界密切相關的系統,但在小型應用、數學計算、符號處理等方面並無優勢。需要指出的是,語言和范式的流行,與大公司支持和商業推動是密切相關的。有人說OOP其實是MOP(Money-Oriented Programming),即以金錢為導向的。雖有過激之嫌,但有經驗的股民都知道,有主力運作的股票總是漲得快一些的。當然OOP能流行,自有獨到之處,誰能說說它到底好在哪裡?”
逗號搶答:“OOP能提高軟件可重用性、可擴展性和靈活性。”
冒號反問:“為什麼過程式編程的可重用性、可擴展性和靈活性就差呢?”
感到來者不善,逗號有點發虛:“因為OOP具有信息隱藏、繼承和多態的特征。”
冒號並不買帳:“首先,將可重用性、可擴展性和靈活性與OOP劃等號,是只見樹木,不見森林——那是所有范式和語言的共同目標。其次,以C語言為例,信息隱藏可用關鍵字static來實現;繼承可用合成( composition)來代替;多態雖然困難些,也有變通之法。更何況這些只是手段而非目的,只要設計合理,C程序同樣具有可重用性、可擴展性和靈活性,性能效率還更優越。即使在OOP日益風行的今天,C的占有率始終穩踞前列,許多大型復雜軟件如操作系統、數據庫等仍以C為主,這足以證明其仍堪大用。”
見逗號有些理屈詞窮,冒號語氣放緩:“請不要誤解,我並非OOP的反對者,相反今後還要重點討論它。但我希望大家少一點照本宣科和人雲亦雲,多一點獨立思考,甚至不妨標新立異。”
稍作停頓,冒號繼續發問:“過程式編程與OOP在設計理念上有什麼區別?”
“過程式編程的理念是重在過程,自頂向下、逐步求精[4]。”引號一出口就自感有些“照本宣科”,見冒號正用鼓勵的目光看著他,這才繼續說下去,“OOP則正相反,重在數據,自底向上、逐步實現。”
冒號首肯道:“如果把整個流程看作一顆倒長的大樹,過程式編程自樹根向下,逐漸分支,直到每片樹葉,類似數學證明中的分析法,即執果索因的逆推法;OOP則從每片樹葉開始,逐漸合並,直到樹根,類似數學證明中的綜合法,即執因索果的正推法。”
句號心領神會:“倘若把樹根看成主函數,離樹根越近,離用戶需求也越近。如果用過程式編程,由於是逆推法,樹干改變容易導致樹枝相應改變,因此一旦用戶需求發生變化,可能會從樹根波及到樹枝甚至樹葉,維護起來殊為不易。相反OOP從樹葉開始設計,離用戶需求較遠,抽象程度較高,受波及的程度較小,因此更易維護和重用。”
冒號拊掌贊道:“好極了!”
問號不解:“您剛才不還說C程序同樣具有可重用性嗎?”
冒號微微一笑:“數學中分析法與綜合法往往是結合起來使用的,過程式編程與OOP也是如此,只不過各有偏重罷了。句號的一番話雖不無道理,但也授OOP的反對者以口實:OOP鼓吹的可重用性來自‘自底向上’的設計模式,而這種模式並非OOP的專利。其實軟件設計的最重要的並不是編程語言,甚至也不是編程范式,而是抽象思維和前瞻思維。關於這一點,我們今後還會詳細闡述。”
歎號不甘寂寞,插言道:“OOP以對象為基本模塊單位,而對象是現實中具體事物和抽象概念的模擬,這使得編程設計更自然更人性化。”
“深合吾意!”冒號揮動著右手,“盡管OOP最大的賣點是其高度的可重用性,相比其他范式卻並不具明顯優勢。但它更接近人類的認知模式,編程者更容易也更樂於用這種方式編程,這是它深入人心的一個重要原因。比較一下兩種用法:牛.吃(草)與吃(牛,草),哪種更接近人類思維?”
有人在底下嘀咕:“如果把牛換成狗,那麼一個是狗吃屎,一個是吃狗屎。”
全班捧腹。
冒號也忍不住笑了:“OOP人性化的另一表現是其接口簡潔易記。看看Win32 API、Unix API等之類操作系統接口或OCI之類的數據庫接口,函數的參數動辄七八個乃至上十個,函數名和數據結構成員也多冗長晦澀,既難記又易錯。相比之下,相應的Java的API顯然平易近人得多。”
問號刨根問底:“為什麼C的API不能象Java的那麼簡潔呢?”
冒號釋疑:“單純這麼比較其實對C並不公平,因為Java的API雖然簡潔易用,但功能上與相應C的API並不等同,換句話說,Java把接口粗粒度化了。”
“接口粗粒度化?”引號質疑道,“就是把一些函數包裝起來吧?我們也可以用C將操作系統、數據庫之類的API再包裝一下。”
“事實上許多軟件公司都曾這樣做過。”冒號颔首作答,“但C函數不像Java對象,本身沒有狀態,只有依靠參數傳遞或外部變量來維持相關函數之間的聯系,包裝後的接口肯定不如Java簡潔,但應該比Java高效。說白了,OOP就是將相關的函數用數據粘合,重新包裝後再貼上對象的標簽。從這種角度上看,與其說OOP更具重用性,不如說更具易用性。”
歎號狐疑道:“OOP並不更具重用性?這可是它的金字招牌啊!”
冒號冷哼一聲:“不要被金字招牌晃暈了眼,我來問你:是收音機、電視機之類的電器產品更具重用性呢,還是與電阻、電容之類的電器元件更具重用性?”
“當然是電器元件啦。”歎號沖口而出。
冒號因勢利導:“每個電器元件具備單一的功能,正如過程式中的函數;每個電器產品是對多個相互關聯的電器元件的封裝,正如OOP中的對象。同樣的電器元件可用於不同的電器產品,具有高度的可重用性,而電器產品重用性低,但易用性高。”
眾人猶自將信將疑。
“對一個沒有獨立思考習慣的人來講,與其說他認同一個理論,倒不如說他認同該理論倡導者的權威。而在他仰視權威的同時,也把自己的思想交托給了權威。”冒號頗具犬儒之風,“你們可以懷疑我的觀點,但絕不可放棄自己的思考。請注意,這就是我在第一堂課提到的精神——批判精神。”
冒號這時停了下來,與在座的每位逐一對視。他仿佛想通過目光把這種精神注入到每個人的身上,就像武俠小說中通過手掌將內功傳輸給他人一樣。並不是每個人都能理解冒號的用心,但都或多或少地感受到一種異樣的氣氛。
“關於OOP今天就談到這裡。”冒號恢復了常態,“請不要奇怪為何如此流行的編程范式我卻一帶而過,那是因為你們對它相對比較熟悉,而我們這一輪只是在作熱身運動,以後再作專項訓練。在結束之前,我們引進一個新視點:過程式編程的模塊以函數為單位,OOP的模塊以對象為單位,二者的區別是:函數是被動的實體,對象是主動的實體。過程式程序的世界是君主制的,主函數是國王,其他函數是臣民,等級分明,所有臣民在聽命於上級的同時也對下級發號施令,最終為國王服務;OO程序的世界是民主制的,所有對象都是獨立而平等的公民,有權利保護自己的財產和隱私並向他人尋求服務,同時有義務為他人提供承諾的服務,公民之間通過信息交流來協作完成各種任務。”
眾人頓覺耳目為之一新。
,插語
[1]所謂基於對象的,有兩種不同的涵義。一種指“限制版”的OOP,即具備對象概念,但不具備OOP的一些其他特征,如繼承或多態等。Visual Basic正屬於此類。另一種指基於原型的(prototype-based),或者說基於實例的(instance-based),而不像通常OOP是基於類的(class-based)。JavaScript、NewtonScript、MOO等語言即屬此類。
[2]這裡所謂“純粹的OOP”並非指一般意義上的“pure OOP”(即所謂的“一切都是對象”),而指單純的、不含其他范式的OOP。
[3]出自Fred Brooks的著名文章《no silver bullet》。他認為沒有一項技術或管理方法的發展能保證,在十年內讓軟件的生產力、可靠性或簡潔性等方面提高一個數量級。常用來泛指沒有一項軟件技術或方法是萬能的。
[4]更准確地說,這是前文提到的結構化編程思想。
。總結
OOP大多是命令式的,但也有函數式的和邏輯式的OO語言。
OOP的核心思想可以歸納為:以數據為中心組織邏輯,將系統視為相互作用的對象集合,並利用繼承與多態來增強重用性。
OOP既不能脫離其他范式,也絕非適用於一切應用。
可重用性、可擴展性和靈活性是所有范式和語言的共同目標,並非OOP所獨有。
與其說OOP更具重用性,不如說更具易用性。
過程式編程重在過程,自頂向下,逐步求精。
對象式編程重在數據,自底向上,逐步實現。
過程式程序的世界是君主制的,OO程序的世界是民主制的。