被繼承的對象稱為基底類別(Base)、超類別(Super)或親類別(Parent);繼承者稱為衍生類別(Derived)、次類別(Sub-)或子類別(Child)。
繼承的目的,是要達到「程式碼再用」(Code Reuse)或「介面再用」。而繼承的手段,就是「擴充」或「修改」。這是重點,請務必牢記。
繼承所導致的程式碼再用,是指次類別能自動沿襲超類別的所有程式碼,好讓你可以不用寫太多程式碼,只需要稍微擴充或修改,就能符合你的需求。
「擴充」指的是定義新的方法(Method);「修改」指的是針對超類別中的某方法重新定義其行為。
請注意,繼承所產生的次類別和其超類別之間,兩者在記憶體內是獨立的。繼承所做的擴充與修改,並不會影響到超類別。在Windows程式設計中,有所謂的SubClassing技巧,這其實不是繼承的概念,因為它會修改到原本類別的記憶體。
繼承所導致的介面再用,是為OO的下一個階段(也就是多型)作準備。介面再用,搭配方法的修改,就形成了多型。
如果你不想再用程式碼,也不想再用介面,或者說你不進行擴充、也不進行修改,那麼透過繼承產生次類別,幾乎是沒有意義的。
唯一的一個小小意義是,次類別和超類別是不同的類別,你可以在程式中依據這一點做判斷,做不同的行為。但這是一種瑣細的程式技巧,和OO無關,而且OO也不鼓勵你這麼做。對OO來說,透過多型的機制造成行為的差異,才是正確的作法。但即使是為了此目的,我們也會使用空介面當作特殊標籤(Mark),而不會使用類別當作標籤,因為介面當標籤的副作用小,成本低,且不是垂直的關係。
將許多類別之間的繼承關係,繪製成一張關係圖,如果繪製時依循「超類別在上,次類別在下」,或者「超類別在左,次類別在右」,就可以形成一個類別階層(Class Hierarchy)。
由於大多數的類別階層設計都是採用單一繼承(Single Inheritance),而非多重繼承(Multiple Inheritance),所以階層圖往往是樹狀結構,符合樹狀結構的階層圖,也稱為繼承樹、類別樹。
多重繼承與介面
單一繼承指的是,只有一個超類別;多重繼承指的是,具有多個超類別。應用框架設計幾乎都是採用單一繼承(例如MFC、.NET Framework、Borland VCL、AIR),只有極少數以前的設計會採用多重繼承(例如Borland OWL)。
不僅如此,連語言本身的設計上,也往往禁止多重繼承(例如Java、Delphi、C#、VB.NET),只剩下極少數語言允許多重繼承(例如C++、Eiffel)。這個趨勢似乎會延續下去,主要是因為多重繼承「可能」會造成「不知道繼承的方法,是來自那個超類別或祖先類別的困擾」。C++要求編程員主動指明繼承的方法來自何處,但Eiffel的作法則更巧妙(請參考www.eiffel.com)。
姑且不論多重繼承的缺點,它的表達能力顯然比單一繼承更好,至少,不少原本在單一繼承時必須透過AOP(Aspect-Oriented Programming)解決的問題,在多重繼承之下可以不需要AOP就能輕易解決。
從Java開始,多數語言使用Interface來解決多重繼承的問題,它們號稱「利用介面可以享用多重繼承的優點,又沒有多重繼承的困擾」。但事實根本不是如此!
介面只能讓你繼承到介面,無法繼承到程式碼(介面不帶程式碼)。如果你在Java中繼承多個介面,你必須親自定義所有介面的每個方法,也就是說,你必須寫許多程式碼。但如果在C++/Eiffel中,你可以繼承許多類別,便不需要再定義這些方法。
所以介面是在「捨棄多重繼承缺點的同時,也捨棄了多重繼承的優點」。也就是說,介面捨棄了「程式碼再用」,保留了「介面再用」。從這個角度來看,「介面再用」比「程式碼再用」更重要。這是因為多型的緣故,多型才是OO的終極目的。
其他和繼承相關的問題
繼承在某些程度上破壞了一部分的封裝,造成次類別和超類別的相依程度提高。超類別如果改變,但是次類別沒有跟著做出改變,可能會造成次類別出問題,類似DLL Hell的觀念。
法律上有所謂的「限定繼承」與「拋棄繼承」,目前的編程語言似乎都沒有這樣的概念,就算有,權力也是放在超類別上,而不是在次類別上。
設計繼承時,必須先考慮介面是否共享,而後考慮程式碼是否共享,再考慮分類。但是經驗不足的編程員,反倒會先考慮分類和程式碼再用,而忽略了「介面再用」是其中最重要的事。(未完待續)
蔡學鏞-專職作家
清華大學資訊工程碩士,曾任華碩集團軟體工程師、元智大學資訊系講師、美商歐萊禮出版社技術編輯、臺灣微軟特約專欄作家。
熱門新聞
2024-12-22
2024-12-20
2024-12-22
2024-12-20