php 多繼承的是很混亂的,二義性和Diamond of Death 兩頭小中間大的繼承樹, 所以使用trait 替代(清晰明了, 實現了複用代碼,扁平化的方式 更加清晰, 解決了二義性(需要執行使用什麼方法不然報錯)。
昨天晚上和G討論一個問題是否使用多重繼承的問題,當時我是極力反對,因為多重繼承在我的影響中式極差的,認為這是一個應該讓人唾棄的。主要原因是我認為它十分容易引起各種各樣的混亂,編程上的和設計上的。 但對於它具體情況並不是很瞭解,因為我重來沒有用過多重繼承,這是因為考試的原因,瞭解過這個東西,考完了馬上就丟掉了。為了具體瞭解這個東西,我上網百度了下,找到了福士對於它的評論。 優點: 對於它的優點,是顯而易見的,簡單,清晰,更有利於複用。雖然單一繼承也能有利於複用,但是還是有可能有很多的重複代碼。如果使用了多重繼承,重複代碼會減少很多。比如百度百科裡提到了一個例子,一隻貓,可以同時繼承於哺乳類和卡通類,這樣就不用再去重新寫卡通類的屬性和方法。 缺點: 缺點也很明顯,首先是二義性,兩個基類中有同名方法的時候,你不得不在子類的調用中指明此方法出自那個基類。這看起來有些麻煩,幸好在你迷糊的時候,編譯器會提醒你。其次:假如類A派生了B和C,而B和C共同派生了D,麻煩就出現了。這種中間大兩頭小的繼承樹有個形象的名字:鑽石型繼承樹(DOD:Diamond Of Death)。從“Death”字面看這是個非常不好的單詞,事實也如此,A是D的父類,但是有兩條路徑。這樣的資料群組織方式會有一些難以預料的後果。除去二義性不說,想想吧,D中有多少個看似重複的方法,有多少個名字相同的資料成員! “ 不惜一切代價,避免DOD的出現。除非,你認為DOD出現在這裡是最恰當不過的,而且,確保你你使用了虛基類(虛繼承),確保你對每個類的細節都完全清楚,確保你知道虛基類(虛繼承)的副作用。 ” 這裡再引進一個詞彙,vtable(c++裡的,PHP有沒有不是很清楚,沒有百度到,不過應該有吧。) Vtable 虛表。 每一個有虛函數的類都有這樣一個東西。 它實際上記錄了本類中所有虛函數的函數指標,也就是說是個函數指標數組的起始位置。 比如virtual void TheSecondFun()記錄在數組的第二個元素,當一個該類的對象執行個體調用TheSecondFun時就根據對應關係把第二個函數指標取出來,再去執行該函數,這種行為叫晚綁定,也就是說在運行時才知道調用的函數是什麼樣子的,而不是在編譯階段就確定的早綁定。多重繼承還會使得子類的vtable變得不同尋常。單繼承的vtable只是在父類vtable的表尾加上新的虛函數,子類對象的vtable中包含了有序的父類vtable。而對於多重繼承,兩個父類可能有完全不同的vtable,因此,子類的vtable中絕對不可能包含完整的有序的兩個父類的vtable。子類的vtable中可能包含了兩塊不相連的父類vtable,因此每個父類都被迫追加了一個vtable,也就是,每個父類的對象都添加了一個指標。 多重繼承還會帶來一些其他的問題:使用父類指標指向子類對象變成了一件複雜的事情。你不得不用到C++中提供的dynamic_cast來執行強制轉換。至於dynamic_cast,也是個麻煩的傢伙,它是在運行期間而非編譯期間進行轉換的(因為編譯期間它不能確定到底要轉向一個什麼類型),因此除了會帶來一些輕微的效能損失,它要求編譯器允許RTTI(Runtime Type Information,運行時類型資訊),也就是要求編譯器儲存所有類在運行時的資訊。 總結下:實際生活中,一些事物往往會擁有兩個或兩個以上事物的屬性,為瞭解決這個問題,引入了多重繼承的概念。 多重繼承的優點是對象可以調用多個基類中的介面。 多重繼承的缺點是容易出現繼承向上的二義性,並且增加了程式的複雜度。 建議: 明智而謹慎地使用多重繼承 Use multiple inheritance judiciously. 最後的最後:存在即合理吧。