《程序世界》筆記2 面向對象2

動態語言和靜態語言

靜態語言

:像Java這樣規定變量和算式類型的語言稱為靜態語言。不通過執行就可以發現類型不匹配這樣的錯誤是靜態語言的一個優點。為了實現多態性,靜態語言被設計成這樣,當給一個類變量賦值時,既可以用這個類的對象來賦值,也可以用這個類的子類對象來賦值。(也就是里氏替換原則)。這樣可以實現多態性。

動態語言:動態語言對數據類型採取了另一種解決方法。類似於鏈表的實現方式,也就是對象本身記錄有關自己種類的信息。這個思想的始祖是Lisp語言。(昨天我花了三個小時學習了這個語言,被深深的震撼到了。發張圖大家來感受一下它的獨特魅力。)

《程序世界》筆記2 面向對象2

lisp解釋器的全部代碼

動態語言允許調用沒有繼承關係的方法,也就是傳說中的鴨子類型(DuckTyping)。程序有沒有錯誤只有執行了才知道。那麼什麼叫鴨子類型呢。

DuckTyping(鴨子類型)

If it walks like a duck and quacks like a duck, it must be a duck.

(走起路來像鴨子,叫起來也像鴨子,那麼它就是鴨子)。

鴨子類型的意義在於,即便一個類不是另一個類的子類,但是它只要擁有另一個子類的方法,做另一個子類方法類似的事情,那麼他就可以被當作另一個類的子類進行多態調用。


繼承的兩種含義:

一種是“類都有哪些方法”,也就是說這個類都支持什麼操作,即規格的繼承。另一種是,“類中都用了什麼數據結構和什麼算法”,也就是實現的繼承

靜態語言中,這兩者的區別很重要。在Java中,實現的繼承用extends繼承父類,規格的繼承用implements來指定接口。但接口不能共享實現,這是個大問題,(注:就是所謂的虛方法,到時候還得自己在類中實現)

動態語言中,區分這兩種繼承意義不大。因為,動態語言採用鴨子類型。因此動態語言就沒有繼承規格這種概念,動態語言要解決的就是有層次的實現多重繼承。

從多重繼承變形而來的mix-in。這是ruby語言的特點,而python語言在編程時推薦使用這樣的編程思想。ruby語言中通過include關鍵字在類中繼承mix-in。而python在定義類的時候,classClassName(Parent,Mixin1,Mixin2)是一種多重繼承,但編程時可以將第二個之後的父類定義為一種方法的集合,這就實際上相當於ruby語言的模塊(module)了。

《程序世界》筆記2 面向對象2


兩個誤解:

第一個誤解:對象是對現實世界中的具體物體的反應(錯)

面向對象編程語言的共通功能:封裝、數據抽象、多態。

結構化編程通過整理數據流,提高了程序的生產效率和可維護性。同樣,面向對象編程通過對數據結構的整理,提高了程序的生產效率和可維護性。

如果把面向對象編程看做是對結構化編程的擴展,那麼對象是否是現實世界中具體物體的反映就不重要了。實際上,面向對象編程語言中的對象,像字符串、數組和範圍等,很多都沒有現實世界中的具體物體與之對應。即使現實世界中有具體物體與之對應,對象也只是描述現實物體某一側面的抽象概念而已。比如貓有顏色、血統等很多屬性,而程序中的對象並不需要把這些屬性都考慮進去。程序只是處理抽象數據的。

對象的模版=類

利用模塊(已有代碼)的手段=繼承

因為繼承只不過是抽象的功能利用方法,所以不必把對繼承的理解束縛在“繼承是對現實事物的分類的反映”。實際上這樣的想法反而妨礙了我們對繼承的理解。

第二個誤解:多重繼承是不好的(錯)

對於像Java或者C++這樣需要指定變量類型的靜態語言來說,父類類型的變量可以用子類的對象來賦值。如果用子類以外的對象來賦值的話,就會發生編譯錯誤。所以可以說這既實現了多態性,又實現了對變量類型的檢查,是一個很好的想法。

但結果是,靜態語言中可以實現多態性的只是限於擁有共通父類的對象。

所以,為了解決這個問題,多重繼承在靜態編程語言中是必要的。接口和類都是為了實現多重繼承的技術,接口的出現是為了限制多重繼承,防止複雜的繼承結構。

動態語言同樣需要多重繼承。mix-in只是實現多重繼承的一個技術。

總結:

為了使程序擺脫goto語句帶來的混亂,我們發明了結構化編程。為了使程序能構更好的對數據結構進行整理,不重複使用代碼,我們發明了對象和類這個概念。為了更好的利用更多已有代碼,我們需要多重繼承,為了限制多重繼承的混亂,我們發明了接口,mix-in等技術。


分享到:


相關文章: