《松本行弘的程式世界》精彩書摘

李鬆峰發表於2011-09-06

寫在前面

以下是2011年4月份在複審《松本行弘的程式世界》期間,隨手摘錄發表在微博上的精彩書摘。雖然都是隻言片語,也沒有上下文,但幾乎哪一段話讀下來,都有讓人猛然醒悟的感覺。我們常說“與君一席話,勝讀十年書”。透過這寥寥數條簡短的書摘,不難窺見《松本行弘的程式世界》是一座多麼令人垂涎的程式設計思想寶庫,更不難想象與身為Ruby語言之父的松本君促膝長談會給你帶來什麼驚喜了。

Enjoy!

  • 像結構化程式設計解決GOTO語句的問題一樣,引入受限多重繼承是一個好方法。在Java語言中,這個方法就是介面,在Lisp或Ruby中,就是Mix-in。

  • 單一繼承只能有一個父類。在現實中,一個公司職員同時也可能是一位父親,一個程式設計師同時也可能是一位作家。於是,多重繼承的思想就這樣誕生了。多重繼承與GOTO語句比較相似,但因其極為靈活會導致複雜性。多重繼承除了會導致結構複雜,還會導致優先順序模糊、功能衝突的問題。

  • 有了資料抽象,程式處理的資料就不再是單純的數值或文字這些沒有具體內容的東西,而變成了人腦容易想象的具體事物。而程式碼的“抽象化”則是把想象的過程“具體化”了。這種智慧資料可以模擬現實世界中的實體,因而被稱為“物件”,物件導向程式設計也由此得名。

  • 程式裡不僅包含控制結構,還包含要處理的資料。結構化程式設計雖然降低了流程複雜性,但隨著資料增加,程式複雜性也會上升。物件導向程式設計就是作為對抗資料複雜性的手段出現的,其核心是資料抽象。資料抽象就是要使用事先定義的方法運算元據,資料和處理方法結合起來成為一個黑盒子。

  • 針對程式控制流的複雜問題,結構化程式設計採用了限制和抽象化的武器。實踐證明,結構化程式設計是成功的,現在幾乎所有程式語言都支援結構化程式設計,結構化程式設計已經成了程式設計的基本常識。

  • 在結構化程式設計出現之前,可以用GOTO語句來控制程式的流程。而結構化程式設計只用順序、分支和迴圈3種語句來控制流程。一方面降低了程式流程的複雜性,另一方面引入了較為抽象的處理塊(例程)的概念,也就是把基本相同的處理抽象成例程,其中不同的地方由外部傳遞進來的引數來對應。

  • 軟體開發的最大敵人是複雜性。最初挑戰這種複雜性的是“結構化程式設計”。結構化程式設計的基本思想是有序地控制程式執行流,即把程式執行順序限制為順序、分支和迴圈這三種,把共通的處理歸結為例程。

  • 物件導向程式設計方法和程式語言一樣在不斷地演變發展。1994年,當時主要的物件導向分析和設計方法Booth、OMT以及OOSE的發明人Grady Booch、Jim Rumbaugh和Ivar Jacobson合作設計了UML。UML是用來描述通過物件導向手法設計的軟體模型的圖示方法,也是利用該方法法分析設計的方法論。

  • 物件導向的發展歷史5。強調與C語言相容的C++能夠寫低階的方法,這樣有利也有弊。為了克服低階語言的缺點,20世紀90年代Java語言應運而生。Java放棄了和C的相容性,增加了Lisp中一些好的功能。此外,通過Java虛擬機器,Java程式可以不用重新編輯而在所有作業系統中執行。

  • 物件導向的發展歷史4。1980年代,世界上很多地方都在研究物件導向。貝爾實驗室的Bjarne Stroustrup為了把C語言的高效率和Simula的物件導向結合,在C語言中追加了物件導向功能,開發了“C with Class”,也就是後來的C++。

  • 物件導向的發展歷史3。麻省理工學院及周邊地區在Lisp語言中發展了物件導向思想。Lisp有濃厚的數學背景,本身有很強的擴充套件性,由此產生了很多創新。多重繼承、Mix-in和Multi-method等等,許多重要的物件導向概念都是從Lisp的物件導向機制中誕生的。

  • 物件導向的發展歷史2。Simula的物件導向思想被廣泛傳播。1980年初,施樂PARC開發了Smalltalk程式語言,旨在讓“兒童也可以使用”。在Lisp和LOGO設計思想基礎上,Smalltalk吸取了Simula的物件導向思想並獨創一格,而且還有一個很好的GUI。Smalltalk的創新使世人開始肯定物件導向。

  • 物件導向的發展歷史。物件導向程式設計思想起源於瑞典20世紀60年代誕生的模擬程式語言Simula。表示模擬物件的資料和模擬方法互相獨立,需要分別管理,程式設計師必須將兩者正確結合,負擔很大。Simula引入了資料和處理資料的方法自動結合的抽象資料型別。隨後又增加了類和繼承的功能。

  • 多型性、資料抽象和繼承,被稱為物件導向程式設計的三大原則。這三項原則通常也會有別的稱謂。例如,把“多型性”稱為“動態繫結”,把“資料抽象”稱為“資訊隱藏”或“封裝”。雖然名稱不同,但內容都是相同的。

  • 物件導向程式語言中最重要的技術是“多型性”。多型,就是可以把不種類的東西當作相同的東西來處理。多型性的優點一是各種資料可以統一處理,二是可以根據物件不同自動選擇最合適的處理方法,三是如果有新資料需要對應,簡單追加即可,不用改動原來的程式。多型提高了開發效率。

  • 演算法和特定的資料結構關係很大。所以一位計算機先驅(Pascal之父Niklaus Wirth)曾經說過:“程式就是演算法加資料結構。”設計模式是指設計軟體時,根據以前的設計經驗而對設計方法的分類。演算法和資料結構從廣義上來說也是設計模式的一種分類。有名的分類(設計模式)有23種。

  • 演算法是解決問題的方法。現實中各種演算法都已經廣為人知了,所以程式設計時的技巧也就是對這些演算法的具體應用。有很多演算法,如果單靠自己去想是很難想出來的。比方說陣列的排序就有很多演算法,如果我們對這些演算法根本就不瞭解,那麼要想做出高速排序程式會很困難。

  • 具體來說,繼承就是在保持既有類的性質的基礎上而生成新類的方法。原來的類稱為父類,新生成的類稱為子類。子類繼承父類所有的方法,如果需要也可以增加新的方法。子類也可以根據需要重寫從父類繼承的方法。

  • 隨著軟體規模的擴大,用到的類的個數也隨之增加,其中也會有很多性質相似的類。這就違背了我們多次提到的DRY原則。程式會變得重複而且不容易理解。修改程式的代價也會變高,生產力則會降低。所以,如果有把這些相似的部分彙總到一起的方法就好了。繼承就是這種方法。

  • 跟原型不同,物件導向程式語言的類和物件有明顯區別,就像做點心的模具和點心的區別一樣,又如整數的類和1這個物件、狗類和名字是poochy這條狗的區別一樣。為了清晰地表明類和物件的不同,物件又常常被稱作例項,叫法雖然不同,但例項和物件是一樣的。

  • 同樣的物件大量存在的時候,為了避免重複,可以採用兩種方法來管理物件。一種是原型,即用原始物件的副本來作為新的相同的物件。Self、Io等語言採用了原型。有名的程式語言使用原型的比較少,很意外的是,JavaScript也是用原型的。另一種是模板,在物件導向程式語言中稱為類。

  • 重複的程式是冗餘的。人們解讀程式、理解程式“意圖”的成本也會增加。請記住,計算機是不管程式是否難以閱讀,是否有重複的。然而,開發人員要閱讀和理解大量的程式,所以程式的可讀性直接關係到生產力。重複冗長的程式會降低生產力。

  • 如前所述,程式的重複是一切問題的根源。重複的程式在需要修改時,所涉及的範圍就會更廣,費用也就更高。當多個重複的地方都需要修改時,哪怕是漏掉其中之一,程式也將無法正常工作。所以重複降低了程式的可靠性。

  • 一個子類只能有一個父類,這稱為“單一繼承”。從自頂向下的方法來看,通過擴充套件一個類生成新類是很自然的。但是,從自底向上的方法提取共通部分的角度來看,一個子類只能有一個父類的限制是太嚴格了。一個子類可以有多個父類,這稱為“多重繼承”。

  • 利用現有的類派生新類的方法稱為“差分程式設計法”。通過抽象把共通的部分提取出來生成父類,與利用已有的類來生成新類,是同一手法的兩種不同表現形式。前者稱為“自底向上”法,後者稱為“自頂向下”法。

  • 繼承有兩個含義:一個是“類都有哪些方法,支援哪些操作”,即規格的繼承;另一個是“類中都用了什麼資料結構、什麼演算法”, 即實現的繼承。在靜態語言中,這兩者的區別很重要,比如在Java中,實現的繼承用extends表示繼承父類,而規格的繼承用implements來指定介面。

閱讀提示:自底向上

相關文章