看板 CompBook 關於我們 聯絡資訊
【Re: 繼承的特性 ??】 侯捷 [email protected] http://www.jjhou.com 2000.04.16 第一次發表於 清大.楓橋驛站(140.114.87.5).物件導向版(Computer/oop) 本文將於日後整理於 侯捷網站/散文 2000 ---------------------------------------------------------------- 這裡有一封 BBS/News OOP 版的貼信,引起我的興趣。 原信如下: -------------------------------------------↓ 發信人: [email protected] (自是溫柔最傷人), 看板: oop 標 題: Re: 繼承的特性 ?? 發信站: 元智大學風之塔 (Wed Apr 12 22:01:15 2000) > >有時繼承的使用,並不遵守現實上的觀念 > >例如: > >圓是一種橢圓 > >所以繼承時,應該要先寫一個橢圓 > >然後寫一個圓去繼承橢圓 > 這種繼承的方式有可能會違反 Liskov-Substitution Principle > >所以 橢圓 比 圓 多了一個 data member > >故我們會寫成 > >橢圓繼承了圓 > 這種方式只是為了繼承圓的 implementation 更是錯誤示範. > 既然橢圓不是圓就不該繼承圓. > 在這個情形下最好圓跟橢圓不要互相繼承, 但可以讓兩者 > 有一個共同繼承的 base class. 恩...王蟲兄所言甚是 當初上 侯 Sir 開的課時, 就覺得他舉這個例子怪怪ㄉ 但一直找不到什麼說法來解釋 現在總算一語道破了說 也就是說 繼承樹就會變成 Shape | ------------------- | | Basic Rect Basic Ellipse | | -------------- ---------------- | | | | Rect Square Circle Ellipse 恩恩恩恩..... 可是這樣會不會增加管理的複雜度啊.... ---------------------------------------------↑ 以下是侯捷的感想: 1. 上課教學所舉例子,有時候視當時使用時機(是否為主講重點, 或只是為了有個繼承體系以做其他講題用途)、講課對象的程度 (是否有一半以上的人從未聽聞所謂 interface inheritance), 而有不同深度的討論。如有同學程度超過老師的預期,宜於上課或 下課繼續追問疑點,可避免讓自己的疑惑一直成為疑惑。 2. 《多型與虛擬》書中亦曾舉過類似上述「以橢圓繼承圓」的例子, 確屬粗糙。然亦不可能於當時剖析 implementation inheritance 和 interface inheritance 的區別。因為當時的情況是希望給一個 「儘量簡化」的繼承體系,以做為「多型」之操練對象。 3. 粗糙就是粗糙,所以比較好的作法是在舉「以橢圓繼承圓」的例子 時,加註少量補充說明與提醒。我將於《多型與虛擬》2/e 中這麼做。 4. inheritance 是個複雜的題目,涵蓋 public/protected/private base classes,virtual/nonvirtual base classes;virtual/nonvirtual member functions。欲釐清所有這些觀念,以網路上的簡短討論做為 學習之道並非智舉。最佳辦法就是找一本好書。 5. 推薦一本好書:Effective C++ 2/e,其中有一章專論 「繼承機制與物件導向設計(Inheritance and Object-Oriented Design)。 內容包括: ---------------------------------------------↓ 條款35:確定你的 public inheritance 模塑出 "isa" 的關係 Item 35: Make sure public inheritance models "isa." 條款36:區分「介面繼承(interface inheritance)」和 「實作繼承(implementation inheritance)」 Item 36: Differentiate between inheritance of interface and inheritance of implementation. 條款37:絕對不要重新定義一個繼承而來的非虛擬函式 Item 37: Never redefine an inherited nonvirtual function. 條款38:絕對不要重新定義一個繼承而來的預設參數值 Item 38: Never redefine an inherited default parameter value. 條款39:避免在繼承體系中做 cast down(向下轉型)的動作 Item 39: Avoid casts down the inheritance hierarchy. 條款40:透過 layering(分層技術)來模塑 has-a 或 is-implemented-in-terms-of 的關係 Item 40: Model "has-a" or "is-implemented-in-terms-of" through layering. 條款41:區分 inheritance 和 templates Item 41: Differentiate between inheritance and templates. 條款42:明智地運用 private inheritance(私有繼承) Item 42: Use private inheritance judiciously. 條款43:明智地運用多重繼承(multiple inheritance,MI) Item 43: Use multiple inheritance judiciously. 條款44:說出你的意思並瞭解你所說的每一句話 Item 44: Say what you mean; understand what you're saying. ---------------------------------------------↑ 6. 在「正方形和矩形」(或是橢圓形和圓形)的繼承關係上, Effective C++ 2/e item35 有這樣的敘述(摘錄): ---------------------------------------------↓ p158~p160 : 或許你會說,你對於鳥類實在缺乏直覺,但是你的幾何學學得不錯。 喔,是嗎?我想說的是,正方形和矩形之間可能有多麼複雜? 好,請你回答這個問題:class Square 應該以 public 的方式繼承 class Rectangle 嗎? 圖 『咄!』你說,『當然應該如此!每一個人都知道正方形是一種矩形, 反之則不一定』。這是真理,至少高中數學課本是這麼教的。但是我 不認為你現在還是高中學生。 考慮這份碼: .... 現在再考慮這份碼,其中使用 public inheritance,允許正方形 被視為一種矩形: .... 歡迎來到 public inheritance 的奇異世界。你在其他領域(包括數學) 學習而得的直覺,在這裡恐怕無法如預期般地有所表現。本例的根本困難 是,某些事情可施行於矩形身上(例如寬度可單獨被修改),卻不可施行 於正方形身上(其寬度總是應該和高度一樣)。但是 public inheritance 主張,每一件事情只要能夠施行於 base class objects 身上 — 每件事情唷! — 就一定可以施行於 derived class objects 身上。 在正方形和矩形的例子中(另一個類似的例子是 sets 和 lists,見條款40), 那樣的主張無法保持,所以以 public inheritance 來模塑它們之間的 關係是錯誤的。編譯器會讓你通過,但是一如我們所見,這並不保證程式 的行為適當(或正確)。每個程式員都必須學得一個教訓:程式碼通過 編譯,並不表示就可以正確運作。 現在,不要因為你發展經年的軟體直覺在與物件導向觀念 打交道的過程中失去效用,便心慌意亂起來。那些知識 還是有價值的,但現在你已經為你的軍械庫加上 inheritance(繼承) 這支大砲,你也必須為你的直覺添加新的洞察力,以便引導你適當地運用 inheritance 這支神兵利器。當有一天有人展示一個數頁長的函式給你看, 你終將回憶起「令 Penguin 繼承 Bird,或是令 Square 繼承 Rectangle」 的概念和趣味;這種繼承方式有可能接近事實真象,但也有可能不是。 是的,當然,「是一種(isa)」並不是唯一存在於 classes 之間的關係。另兩個常見關係是「有一個(has-a)」和「根據 某物實作出(is-implemented-in-terms-of)」。這些關係將在 條款40和42討論。將上述這些重要的相互關係中的任何一個誤塑 為 isa 而造成的錯誤設計,在 C++ 中實不罕見,所以你應該 確定你確實瞭解這些「classes 相互關係」間的差異,並且知道 在 C++ 中如何塑造它們。 ---------------------------------------------↑ -- the end  -- ※ Origin: 楓橋驛站<bbs.cs.nthu.edu.tw> ◆ Mail: [email protected]