多重繼承不好的觀點是錯誤的 — 小評 松本行弘的程式世界
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
首先得說, 一般某種語言的發明人寫的關於自己語言的東西都是非常值得閱讀的, 從別的牛人那裡你也許能學會很多奇技淫巧, 但從語言發明人那裡你能學到語言發明人本身設計的初衷, 以及設計時的一些抉擇. 這種思路是獨一無二, 絕無僅有的. 所以我在學習一個新語言時, 假如語言發明人有寫書, 一定優先閱讀.
語言發明人寫的書又分兩種, 一種是語言的教材, 這個幾乎是慣例, 因為一個語言在初期, 沒有其他人會用的時候, 語言發明人不教會別人怎麼使用, 那可能就沒有人會用了, 而他們這本教材的好壞甚至能決定他們語言最後能有多流行. 我聽說一個有意思的言論, 說C語言之所以這樣流行, 很重要的原因就是C程式設計語言這本教材實在寫的太好了. 雖然有些誇張, 但是不無道理, 這個和現在一些開源庫對文件要求很高的道理是一樣的, 一個開源庫的文件寫的好不好, 對這個庫最後是不是會被很多人使用有極大的關係. 當然, 也有反例, 鬼知道Anders Hejlsberg是怎麼把一本C#的語言的教材C#程式設計語言
另外一種, 那就是除了教材外, 寫關於自己語言的設計想法的書, 這種書就更加少了, 就目前而言, 我只看到過Bjarne Stroustrup寫的C++語言的設計與演化和這本松本行弘的程式世界. 歡迎大家給我推薦其他的類似書籍. C++語言的設計與演化講述了BS在設計C++時的各種抉擇, 看完該書後, 我第一次明白C++到底是怎麼設計成那麼難用的, 當然, 同時也明白了怎麼使用C++是正確的. 後來還反覆的讀了好幾遍, 工作後, 也極力的推薦給使用C++的同事閱讀, 事實上, 現在我買的這本書就還在我原來的同事那裡, 雖然我已經離開原公司了. 如果說, 那個時候讀
Ruby的繼承體系
多重繼承不好的觀點是錯誤的
– 松本行弘
這是我感覺是全書最精彩的部分, 本文也主要關注這一部分. 特別是對於多重繼承部分的描述, 充分體現了一個優秀的程式設計師, 語言設計者的功底.
講到這裡, 我想先講講我對這部分內容的認識歷史.
在C++語言的設計與演化中, 我們知道, 多重繼承在加入C++之前就有爭議, 但是BS堅持自己的意見, 最終C++還是支援了多重繼承. 並且為被很多人批評的多重繼承進行了辯護, 他的確承認了多重繼承可能帶來的混亂, 但是認為, 在有的時候, 多重繼承的抽象的確是有意義的, 並且這種抽象在有的時候會更加優美. 本著C/C++的設計原則, “程式設計師總是對的”, 不應該人為的去限制程式設計師, BS堅持的提供了多重繼承這一工具, 並讓程式設計師去決定, 什麼時候用會導致混亂, 什麼時候用可以讓設計更加優美.
在成為標準以後, 多重繼承的爭議一直沒有消失, 各種支援多種繼承的語言甚至在怎麼解決菱形繼承上出現了不同的解決方案. 比如C++中, 菱形繼承還引入了一個更扭曲的虛繼承, Python支援多種繼承, 並且因為所有的物件都繼承於共同的基類Object, 導致任何多重繼承其實都是菱形繼承, 當你真的開始自己用多重繼承時, 其實繼承結構已經是一張網了, 對此, Guido Van Rossum的答案是用深度優先搜尋, 靠基類排列的順序來決定, 個人覺得不僅沒有解決這個問題, 反而顯的更加神奇.
而無數的JAVA使用者者則是把C++的多重繼承當作笑話和C++混亂的根源, 自豪的宣佈JAVA沒有這個問題. James Gosling給出了他自己的解決方案, 那就是沒有沒有C++自由的多重繼承, 只支援對實現的單繼承, 並引入了介面, 只允許介面的多重繼承. 並且, 在繼承介面和繼承基類形式上特別加了一些區別, 所以實際中, 很多人並不把這叫做多重繼承, 並宣稱JAVA沒有多重繼承.
有意思的是, 高老頭後來去了Google, 而我使用C++時儘量遵循的規範Google C++ Style Guide中就明確的規定了禁止C++的多種繼承(不代表這兩個事情有任何關係), 並且在Style Guide中自己給出了他們的Interface定義, 只允許使用此Interface進行類似JAVA的介面多重繼承. 這真是趣事, 在一個Style Guide中, 去強行修改一個語言特性, 也足以證明C++的多重繼承有多麼臭名昭著了.
就個人感受, 以前開發的一個iOS遊戲專案, 僅僅開發了半年, 就因為程式碼混亂不堪, bug多到無法維護, 專案一度在另外一個工作室被cancel, 然後我們中途再接手開發, 並且把多重繼承看作是混亂的根源, 後來整理程式碼的很大部分工作就是用元件的方式去替代掉混亂的繼承, 並且改善後設計清晰了很多, 再後來, 我開發一個Android專案的時候, 第一次真的在專案中去使用JAVA, 深刻的體會到了JAVA對繼承的限制, 也真的去實踐了GOF在設計模式中早就提到的”組合優於繼承”這一設計原則, 也去體會了JAVA社群提倡的面向介面程式設計. 事實上, 我也逐漸的感覺到了, 其實, “繼承本身就是一種強耦合”, 就是一種子類對父類的依賴耦合, 在一些書籍中提到, 甚至繼承本身都是不提倡的, 也就是說, 提倡的是基於物件的設計(OB), 而不是面向物件的設計.(OO)
故事到這裡就是我的認識了, 鑑於我的認識都來自於各種上述提到的流行語言和經典書籍, 我想大部分人可能會有和我類似的觀點吧. 但是Mats不這麼看.
Mix-in
本來只是為了跨越繼承層次來共享程式碼, 現在卻需要另外生成一個獨立物件, 並且每次方法呼叫都要轉送給那個物件, 這實在是不太合理, 而且執行的效率也不高.
Mats直說JAVA對多重繼承的解決方式不夠方便, 也不太合理. 他提供的解決方案是, Mix-in.
Mix-in按照以下規則來限制多重繼承.
通常的繼承用單一繼承
第二個以及兩個以上的父類必須是Mix-in的抽象類
Mix-in類是具有以下特徵的抽象類.
不能單獨生成例項
不能繼承普通類
通過這種在介面和普通多重繼承之間的折衷, Mix-in提供了對實現的多重繼承, 同時對其進行了限制, 使得繼承結構不會成菱形, 而是和單一繼承一樣的樹型. 在Ruby中, Mix-in是通過模組(module)的概念來實現的. 作為例子, Matx用了Ruby中的Stream實現的例子.
Ruby的Stream結構
C++的Stream結構
從這點看, 為什麼我說這是一種折衷方案呢, 因為相對於只允許介面的多重繼承來說, 實現更加方便了, 但是, 對於真正的面向物件來說, 畢竟還是需要把一些實現給拆分成更細粒度的類, 才能符合Mix-in的要求. 比如看上面兩個圖就能發現, 同樣的功能, C++需要的類要比Ruby需要的少. 從這點來說, Mats批評JAVA的組合方式為了共享程式碼需要生成一個獨立物件是不方便的, 而實際上Max-in也會相對真正的多重繼承來說需要更多的獨立物件, 只是使用的方式不是組合, 而是繼承. 當然, 這種限制能帶來類似JAVA的更加良好的設計, 避免菱形繼承及類似更加複雜的繼承體系, 同時, 又比JAVA那種方式更加方便, 大家都知道, 加一個繼承只要一個單詞, 而加一個物件的組合呼叫, 往往需要增加N個函式介面, 以及N個呼叫.(雖然有種東西叫做委託) 有趣的事情是, 在程式設計這件事情上, 很多時候, 折衷的方法卻往往是優秀的方法…
本書不足
- 作者在前言裡面介紹, 是在雜誌上連載的文章的基礎上編輯修改來的, 所以有些地方其實能發現明顯的重複, 甚至不僅僅是文字內容重複, 連Enumerable的那張表都原封不動的重複了兩遍.
- 後面的章節趣味性有餘, 但是感覺思想性稍微有些弱. 所以我看的時候過的比較快, 不過這也見仁見智吧, 也不排除我對相關內容興趣不足的因素.
額外的宣告
我在最新的文章中把所有關於書的連結從豆瓣改為亞馬遜了, 假如你點選我的給的連結過去並且買了書, 亞馬遜會給我一些佣金, 當然我知道這沒有多少, 但是聊勝於無吧, 以後的文章類似, 不再聲明瞭. 我甚至都不知道這樣無害的事情為什麼需要宣告, 但是好像在中國你需要這樣做, 因為別人都這麼做了.
writen by 九天雁翎(www.jtianling.com)