天天看點

《UML面向對象設計基礎》—第1章1.7節繼承

本節書摘來自異步社群《uml面向對象設計基礎》一書中的第1章1.7節繼承,作者【美】meliir page-jones,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

1.7 繼承

uml面向對象設計基礎

如果你寫了一個類c,後來又發現一個類d 除一些額外的屬性和操作外與類c幾乎是一樣的,你會怎麼辦呢?一種辦法是簡單地複制c的所有屬性和操作,然後将其放到d中。但這種方法不僅增加了額外的工作,而且複制本身也存在維護的麻煩。更好的方法是讓類d向類c“請求使用其操作”,這種方法稱為繼承(inheritance)。

繼承(從c到d)是指類d在類c中隐式地定義其每個屬性和操作,就好象

這些屬性和操作是在類d本身中定義一樣。

c稱為d的超類。d稱為c的子類。

換言之,通過繼承,類d的對象可以充分利用類c對象的屬性和操作 。

繼承代表着面向對象與傳統系統方法差別的另一個主要方面。使你可以更加有效地構造軟體:

首先,構造類以便處理一般情況。

然後,為處理特殊情況,增加更特殊的類繼承首批建立的類。這些新類就可以使用原來類的所有操作和屬性(包括類和執行個體的操作和屬性)。

下面的例子有助于說明上述原理。假設在航空應用中有一個類aircraft。aircraft可以定義名為turn的執行個體操作,執行個體屬性名為course。

類aircraft處理與任何種類的飛行器有關的活動或資訊。然而一些特殊的飛行器執行特殊的活動,是以需要特殊的資訊。例如,滑翔機執行特殊的活動(如釋放拖鍊)可能需要記錄特殊的資訊(如是否連接配接拖鍊)。

是以,我們可以定義另一個類glider,使其繼承aircraft。glider有一個名為release towline的執行個體操作和一個名為weathertowlineattached的執行個體屬性(屬于類boolean)。圖1.7給出了結構圖,空心箭頭表示繼承。

現在讓我們來設想一段面向對象程式的繼承機制,首先從類aircraft和glider生成對象,然後給這些對象發送消息。在這段程式後面讨論了标記為從(1)到(4)的四條語句。

《UML面向對象設計基礎》—第1章1.7節繼承

① ac表示由ac指定的對象接收消息 turn(newcourse,out turnok) ,執行操作turn (帶有适當參數)。因為ac是aircraft的一個執行個體,ac将直接使用在類aircraft中已經定義的操作turn。

② gl表示由gl指定的對象接收消息releasetowline,執行操作releasetowline(不需要參數)。

  因為gl是glider的一個執行個體,gl将直接使用在類glider中已經定義的操作releasetowline。

③ gl表示由gl指定的對象接收消息turn(newcourse,out turnok),執行操作turn(帶有适當參數)。如果沒有定義繼承,這個消息在執行時會出錯(如無定義操作-turn),因為gl是glider的執行個體,而glider中沒有名為turn的操作。

  然而,由于aircraft是glider的超類,對象gl也被允許使用aircraft的任何操作。如果aircraft有一個超類flyingthing,gl将被允許使用flyingthing的任何操作。是以,标記為(3)的代碼行可以正确運作,在aircraft中定義的操作turn也會被執行。

④ 這行代碼不會執行!ac為aircraft的執行個體,沒有定義名為releasetowline 的操作。繼承在這裡用不上,隻有glider類定義了releasetowline操作,并且glider是aircraft的子類。由于從glider 到aircraft方向沒有繼承,系統将由于錯誤而停止運作。這樣設計是合理的,因為ac可能表示大型噴射機,操作releasetowline對其沒有意義。

在1.6節讨論了類和對象的差別。現在我們知道對象和執行個體之間也存在細微的差别。盡管目前為止我們将“對象”和“執行個體”幾乎同樣對待,在某種意義上繼承允許一個對象同時是多個類的執行個體。

在現實世界中也是如此。如果你擁有一個滑翔機,當然完全擁有一個尾部具有辨別(句柄)的對象。這架滑翔機(顯然)是滑翔機的一個例子,同時也是飛行器的例子。概念上講,你擁有的這個對象是glider的執行個體,也是aircraft的執行個體。

  實際上,上述例子說明了一個有效使用繼承的有效測試。如果可以說“某個d是某個c”,則d差不多就是c的子類。是以,既然可以說“滑翔機是某種飛行器”,則類glider應該是類aircraft(飛行器)的子類,在第10、11和12章将進一步讨論繼承的正确使用方法。

讓我們進一步探讨繼承。gl表示由gl引用的對象在運作時是兩部分的融合。一部分是為glider定義的執行個體操作和執行個體屬性;另一部分是為aircraft定義的執行個體操作和執行個體屬性。如圖1.18所示。

在大多數語言中,繼承子類繼承超類所提供的一切資訊;子類不能有選擇地繼承。但可以通過技巧使子類略去繼承的操作,這将在第1.8節讨論。

《UML面向對象設計基礎》—第1章1.7節繼承

采用好的面向對象語言實作繼承的實際代碼非常直覺。隻需聲明每個子類繼承哪個超類。例如:

這個繼承的例子為單繼承,指每個類至多有一個直接超類。此外,還有多繼承的情形。多繼承指每個類可以有任意多個直接超類。

多繼承能将單繼承的繼承樹轉換為繼承架構,如圖1.19所示。

《UML面向對象設計基礎》—第1章1.7節繼承

多繼承引入了一些設計上的難題,因為子類從多個父輩繼承,可能引起繼承操作或屬性的沖突。沖突的操作具有相同的名字,繼承子類不能容易地表述繼承的是哪個操作。

諸如命名沖突問題影響了多繼承的聲譽。多年來,對多繼承的譴責和贊成已經到了炙熱化的程度。我聲明我是贊同多繼承的,因為現實中需要頻繁使用多繼承的子類。正如圖1.19所示,類passengeraircraft合理地從類aircraft和類pessengervehicle獲得繼承。

因為多繼承可能建立複雜和難以了解的結構,是以,使用多繼承比使用單繼承要更加慎重。目前為止,c++和eiffel兩個主要語言支援多繼承,而另外兩個主要語言java和smalltalk則不支援多繼承。

本文僅用于學習和交流目的,不代表異步社群觀點。非商業轉載請注明作譯者、出處,并保留本文的原始連結。

繼續閱讀