作為程序員,你是使用函數(shù)式編程還是面向?qū)ο缶幊谭绞剑?br />
在本文中,擁有 10 多年軟件開發(fā)經(jīng)驗的作者從面向?qū)ο缶幊痰娜筇匦浴^承、封裝、多態(tài)三大角度提出了自己的疑問,并深刻表示是時候和面向?qū)ο缶幊陶f再見了。
幾十年來我都在用面向?qū)ο蟮恼Z言編程。我用過的第一個面向?qū)ο蟮恼Z言是 C++,后來是 Smalltalk,最后是 .NET 和 Java。 我曾經(jīng)對使用繼承、封裝和多態(tài)充滿熱情。它們是范式的三大支柱。 我渴望實現(xiàn)重用之美,并在這個令人興奮的新天地中享受前輩們積累的智慧。 想到將現(xiàn)實世界的一切映射到類中,使得整個世界都可以得到整齊的規(guī)劃,我無法抑制自己的興奮。 然而我大錯特錯了。
01繼承,倒塌的第一根支柱 乍一看,繼承似乎是面向?qū)ο蠓妒降淖畲髢?yōu)勢。所有新手教程講解繼承時都會拿出最簡單的繼承的例子,而這個例子似乎很符合邏輯。
然后就是滿篇的重用了。甚至以后的一切都是重用了。 我囫圇吞下這一切,然后帶著新發(fā)現(xiàn)興沖沖地奔向世界了。香蕉猴子叢林問題帶著滿腔的信仰和解決問題的熱情,我開始構(gòu)建類的層次結(jié)構(gòu)然后寫代碼。似乎一切皆在掌控中。 我永遠(yuǎn)不會忘記我準(zhǔn)備從已有的類繼承并實現(xiàn)重用的那一天。那是我期待已久的時刻。 后來有了新的項目,我想起了另一個項目里我很喜歡的那個類。 沒問題,重用拯救一切。我只需要把那個類拿過來用就好了。 嗯……其實……不僅是那一個類。還得把父類也拿過來。但……應(yīng)該就可以了吧。 額……不對,似乎還需要父類的父類……還有……嗯,我們需要所有的祖先類。好吧好吧……搞定了。沒問題。 不錯。但編譯不過,怎么回事?哦我知道了……這個對象還需要另一個對象。所以那個也得拿過來。沒問題…… 等等……我不僅需要那個對象,還需要那個對象的父類,和父類的父類,和……包含的所有對象的所有祖先…… 唉…… Erlang 的創(chuàng)建者 JoeArmstrong 有句名言:
面向?qū)ο笳Z言的問題在于,它們依賴于特定的環(huán)境。你想要個香蕉,但拿到的卻是拿著香蕉的猩猩,乃至最后你擁有了整片叢林。
香蕉猴子叢林的解決方法這個問題的解決方法是,不要把類層次建得那么深。但如果繼承是重用的關(guān)鍵,那么給繼承機(jī)制添加的任何限制都會限制重用。對吧? 沒錯。 那我們可憐的面向?qū)ο蟪绦騿T該怎么辦?指望一杯三聚氰胺奶維系我們的健康嗎? 答案就是:包含和委托(Contain and Delegate)。一會兒會詳細(xì)解釋。菱形繼承問題早晚你會遇到下面這種惡心的問題,有些語言甚至根本解決不了。
大多數(shù)面向?qū)ο笳Z言都不支持這種情況,盡管看上去似乎很符合邏輯。為什么面向?qū)ο笳Z言支持這種情況如此困難? 來看看下面的偽代碼:
ClassPoweredDevice{ } ClassScannerinheritsfromPoweredDevice{ functionstart(){ } } ClassPrinterinheritsfromPoweredDevice{ functionstart(){ } } ClassCopierinheritsfromScanner,Printer{ } 注意 Scanner 和 Printer 類都實現(xiàn)了名為 start 方法。 那么問題來了,Copier繼承哪個start?是Scanner的還是Printer的?肯定不可能同時繼承啊。菱形繼承的解決解決方案很簡單:不要這樣做。 沒錯。大多數(shù)面向?qū)ο蠖疾蛔屇氵@么干。 但是,但是……要是必須這樣建模該怎么辦?我需要重用! 那就必須使用包含和委托。ClassPoweredDevice{ } ClassScannerinheritsfromPoweredDevice{ functionstart(){ } } ClassPrinterinheritsfromPoweredDevice{ functionstart(){ } } ClassCopier{ Scannerscanner Printerprinter functionstart(){ printer.start() } } 注意現(xiàn)在 Copier 類包含一個 Printer 實例和一個 Scanner 實例。然后將 start 函數(shù)委托給 Printer 類的實現(xiàn)。要委托給 Scanner 也很簡單。 這個問題是繼承這根支柱上的另一條裂縫。脆弱的基類問題好吧,那我盡量使用較淺的類層次結(jié)構(gòu),并保證里面沒有環(huán),這樣就不會出現(xiàn)菱形繼承了。 似乎一切都解決了。直到我們發(fā)現(xiàn)…… 我前一天工作得好好的代碼今天出錯了!關(guān)鍵是,我沒有改任何代碼! 嗯也許是個 bug……但等等……的確有些改動…… 但改動的不是我的代碼。似乎改動來自我繼承的那個類。 為什么基類的改動會破壞我的代碼? 原來是這樣…… 看看下面這個基類(用Java寫的,但就算你不懂Java,應(yīng)該也很容易看懂):importjava.util.ArrayList; publicclassArray { privateArrayList
-
封裝
+關(guān)注
關(guān)注
127文章
7997瀏覽量
143422 -
編程
+關(guān)注
關(guān)注
88文章
3637瀏覽量
93996 -
C++
+關(guān)注
關(guān)注
22文章
2114瀏覽量
73860
原文標(biāo)題:面向?qū)ο缶幊蹋僖姡?/p>
文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論