1. 程式人生 > >在UML中表示Java繼承和介面

在UML中表示Java繼承和介面

 
繼承


在Java中,我們可以宣告一個類擴充套件(extends)另一個類,還可以宣告一個類實現(implements)一個或者多個介面。下面我們來看看如何在UML中表達這些概念。

下面是三個Java類的基本骨架。第一個類是代表某種支付方式的Payment抽象類,另外兩個類分別擴充套件Payment類,描述兩種不同的支付方式:



圖一用UML顯示了同樣的三個類。在操作和屬性宣告中,型別和引數之類的細節都沒有顯示出來,這是為了更清楚地顯示出類的整體結構以及各個類之間的關係。



圖一:UML一般化關係


Java中的extends關鍵詞聲明瞭繼承關係,相當於UML中的“一般化”(Generalization,也譯為“泛化”)關係,在UML圖形中用子類向超類的實線空心封閉箭頭表示。圖一額外增加了一個Sale類,這是為了更清楚地說明UML一般化關係與UML定向關聯關係所用箭頭的不同。關聯關係與一般化關係的另一個不同之處在於,一般化關係的兩端不需要說明多重性或角色名稱。

顯然,UML類圖比三個Java原始碼檔案更清楚直觀地顯示出了三個類之間的繼承關係。如果你要與別人探討設計思路,繪製UML草圖也要比直接使用程式碼簡單快捷得多。 

也許有人會說,系統的類結構圖就在他們的頭腦中,他們只需要直接使用Java程式碼。實際上,對於規模較大的系統,這種說法顯然是不成立的;即使對於規模較小的系統,如果一定的時間之後要由其他程式設計師修改,沒有UML圖也會寸步難行——很難保證每一個人都瞭解你頭腦中的類結構圖。 

在UML中,抽象類的標誌是類的名字以斜體顯示。

在白板或紙張上手工畫UML草圖時,很難區分字型是否是斜體。為此,一些人建議這些場合可以在類名稱的右下角加上{abstract}標記以示區別。 

另一些人認為,在白板上寫{abstrac t}顯得太羅嗦,他們傾向於打破UML常規,在類名稱的右下角加上一個0表示零個例項,如果在該位置寫上1,則表示該類是一個singleton類(永遠只有一個例項的類);如果在該位置寫上N,則表示它是一個列舉類(擁有固定例項數量的類,如一星期中的天數,彩虹的顏色,等等)。不過,這一切都不是標準的UML,只能用於手工繪製UML圖的場合,看來也不可能得到UML建模工具的支援。 

歷史知識:UML首先由Rational公司的一個工作組發明,Ration公司是UML建模工具Rose的生產者。UML於1995年的OOPSLA會議上被公諸於世,隨後,OMG(物件管理組織)於1997年採用了UML規範。不難理解,繼續負責發展UML規範的OMG任務組包含了來自幾乎所有主流UML工具廠商的代表。因此,除了嚴格遵從規範的UML軟體工具,在一些書籍或網頁上發現不規範的UML符號也不足為怪。 


繼承使得一個類能夠使用另一個類的屬性和方法,就象使用自己的屬性和方法一樣。當這類繼承機制第一次出現時,人們普遍把它視為重用現有程式碼的理想方法。令人遺憾的是,規模過於龐大的繼承樹變得很脆弱,修改繼承樹的一部分,就會在整棵繼承樹中引起一系列的連帶反映。在面向物件的程式設計中,如果要實現有效的封裝,就應該讓改動區域性化,即一個地方的改動不至於引起其他地方的變化。而修改繼承樹一個地方引起其他地方的變化恰恰違背了上述設計思想。UML圖使得我們能夠方便地掌握繼承關係圖,從而為應用繼承關係帶來了方便。那麼,什麼時候適合運用繼承關係呢?按照《Java Design》一書,對於超類A和子類B,執行如下檢查: 

命題“B是一個由A扮演的角色”不成立。 

B永遠不需要變形成為其他某些類別中的物件。 

B擴充套件而不是覆蓋或廢棄A的行為。 

A不僅僅是一個工具類(一些可以重用的實用功能)。 

對於一個問題域(特定的業務物件環境):A和B定義了同一型別的物件,或者是使用者事務、角色、實體(團體、位置或其他東西),或其他物體的相似類別。 

如果上述任意一個判斷不成立,那麼把A和B定義成繼承關係可能是不合適的,改用關聯關係可能更加穩固、正確。例如,圖二違背上面的第一個判斷,因為“僱員是一個由人扮演的角色”成立。另外,它還違背了第二個判斷,因為僱員確實可能改變其類別(身份),例如某個時候它可能是顧客。這樣,一個既是顧客又是僱員的人就要有兩個獨立的物件來描述,從而使儲存在Person類裡面的資訊重複出現,帶來了兩個資料副本之間資料不一致的風險。 

介面 


Java程式語言中介面(Interface)的概念也能夠與UML概念匹配。UML中的介面是一種實現繼承的形式,但這種繼承形式與Java中通過關鍵詞extends實現的繼承有所不同。 

在Java中,extends關鍵詞描述了一種繼承形式,它既繼承介面也繼承行為。這種型別的繼承有時被稱為Sub-classing。與其他的面象物件程式語言不同,Java類只能從一個類繼承。許多時候,設計UML圖的人熟悉多種程式語言,常常會引入多重繼承的思想,例如C++的多重繼承思想。從已有的Java程式碼生成UML圖(這個過程稱為反向工程)不會帶來多重繼承的問題,但如果要求一個Java程式設計師去實現一個帶有多重繼承的UML類圖,就會出現問題。如果多重繼承中的超類是純抽象類,這部分類可以用Java的介面來描述,但是,如果只做這種轉換不足以把UML類圖中的多重繼承全部轉換成單重繼承,這時就必須修改UML類圖重新建模了。 

雖然Java不支援C++之類語言那樣的多重繼承,但它支援實現多重介面。這種由Java關鍵詞implements宣告的繼承只繼承介面,這種繼承有時被稱作Sub-typing。在UML中,實現介面的類與介面定義之間的關係叫做Realization關係,用一個虛線封閉箭頭表示,從實現介面的類指向介面。介面本身的UML圖與普通類一樣,但它的名字上面要加上“<<interface>>”。圖四由圖一修改而成,Payment類被一個介面取代。(關於Realization名稱的說明:Realization最常見的中文譯名是“實現”。但是,Java的implements也叫做“實現”。為避免混淆,本文中凡是出現Realization的地方一律直接使用英文)。 

介面可以從一個或者多個其他介面擴充套件。UML一般化關係(實線封閉箭頭)可用來描述這種關係,如圖五所示。 


UML還支援另一種介面符號,即用圓圈表示介面(加上連線之後就成了棒棒糖的樣子),但這種表示法多用於UML元件圖,在UML類圖中比較少見。 

如果UML圖規模較大,有大量的類實現一個常用介面,整個UML圖可能亂成一團糟。《Java Design》一書提出了一種簡化方法,後來又被《Streamlined Object Modeling》一書的作者採用,這就是在實現介面的類中,用介面的名字替代從介面繼承的方法,不過這不屬於標準方法。遺憾的是,目前似乎還沒有工具支援這種轉換。



結束語:繼承和介面是Java語言中非常有用的機制,我們已經看到,可以用UML的一般化和Realization關係使得Java的這兩個概念視覺化。另外,一些非標準化的表示方法能夠極大地簡化UML圖。在下一篇文章中,我們將瞭解如何在Java程式中保留無法直接表達的UML語義資訊。