類別圖是最難設計得好的一款圖,也是最需要花功夫學習和練習的一款圖,同時它也是UML圖中涵蓋最多概念的一款圖。關於類別圖的指南編號從87到145,共計有五十九條,上一期介紹了類別圖的一般性指南和類別風格,本期接著從「關係」開始。

關係
在類別圖中,除了類別之外,關係是另一個重要的概念,跟它有關的一共有十條,指南編號從112到121。

指南112 關係線水平放置(Model Relationships Horizontally)
原著作者建議繪製關係線時,採用水平線段。我是認為不一定要採用水平線段啦,直的或者橫的直線其實都可以,否則關係線繁多的時候,大量使用水平線段也很佔版面空間的。

指南113 繪製「限定元」(qualifier)的矩形圖示時,要小於類別的矩形圖示(Draw Qualifier Rectangles Smaller than Classes)
「限定元」(qualifier)是UML高級認證的考試範圍,換句話說,它是屬於比較少用得到的概念。當遇到多對多的結合關係時,我們可以使用限定元來限定出一個較小集合的物件群。這樣說有點抽象,我們來看一、兩個範例,你會比較容易懂。

比方說,投資人跟股票的關係是多對多,如圖1所示。因為每一位投資人可以購買多檔股票,而每一檔股票也可以由多個投資人所購買。


在圖2的物件圖中,陳小明投資人購買了中鋼和台積電這兩檔股票。
而就中鋼股票而言,有陳小明和張大強這兩位投資人購買它。再看到台積電的股票,則更受歡迎,一共有陳小明、張大強和李大華這三位投資人購買它。所以,投資人與股票之間就呈現出多對多的結合關係。


如果,我們在投資人這一端設置了一個限定元,使用「公司」當作限定值,就可以限定出投資人名下某一家公司的股票,如圖3所示。
限定元也是採用矩形圖示,跟類別圖示一樣,繪製於結合關係的端點處。編號113指南則提到,繪製限定元的矩形圖示時,要記得畫的比類別的矩形圖示小一些。


特別注意到圖3中,股票端的物件數目減少了,本來在圖2中是多對多的情況,加了限定元之後,變成多對一的情況。使用了限定元之後,變成是「來源端物件+限定值」兩者配對限定出目標端物件,如圖4所示。


來源端物件加上限定值可以限定出較小集合的目標端物件數,但是目標端的物件數目不一定總是「一個」,也可能還是限定出「多個」目標端物件的情況,只不過這個「多個」已經是原先目標端物件的子集(subset)了。

再看另一個投資人與基金的範例,投資人與基金原先是多對多的情況,如果我們配上基金公司當作限定值的話,可以限定出較少的基金物件個數,不過還是多對多的情況,因為一家基金公司通常會發行多檔基金,如圖5所示。


不過,倘若我們改用基金代號做為限定元的話,就可以限定出一個基金物件,因為每一檔基金只有一個基金代號,如圖6所示。

指南114 只有當兩個元素之間有關係時,彼此才能夠合作(Model Collaboration Between Two Elements Only When They Have a Relationship)
無論是結合關係、依賴關係,還是一般化關係,總之,兩個元素之間一定要存在某種關係,彼此之間才能夠合作。

指南115 要建立暫時性的關係的話,使用依賴關係(Model a Dependency When the Relationship Is Transitory)
暫時性的關係是指用完就丟,並不會長期儲存在資料庫中的關係。
在UML類別圖中,依賴關係就屬於拋棄式的暫時關係,而結合關係則是一種會長期保存的靜態關係。

指南116 連到同一個類別的關係線,可以使用樹狀結構來呈現(Tree-Route Similar Relationships to a Common Class)
連到同一個類別的關係線,可以使用樹狀結構來呈現,這樣會讓圖面的關係線顯得整齊畫一,如圖7所示。


指南117 永遠指出個體數目(Always Indicate the Multiplicity)

指南118 避免只使用「無限多」的個體數目(Avoid a Multiplicity of “*”)
一個物件倒底可以連結多少個另一種物件呢?我們將物件可以連結的數目限制,稱為「個體數目」(multiplicity),並標示在兩結合端點上,如圖8所示。下限最小為零,上限最大為無限大,並以星號(*)代表無限大。如果,僅出現一個數目,代表上下限數目相同。

在圖8的範例中,一個信用卡物件最少可以沒有連結到任何刷卡消費物件,最多則可以連接到無限多個刷卡消費物件。至於,一個刷卡消費物件不僅只能,而且是一定要,連結到一個信用卡物件。


在個體數目為一的情況下,有些開發人員會省略、未標示出個體數目,指南117則是建議,千萬別省略個體數目,即便個體數目是一的情況。

再者,如果個體數目只標示星號(*)時,意味著所有數字的個體數目都被允許,也就是說,可以是零到無限多(0..*)的情況。不過,指南118則是認為別僅用一個星號,這樣容易令人混淆,分不清到底是零到無限多(0..*)、還是一到無限多(1..*)呢?總之,把下限和上限數目仔細標示清楚,別偷懶省略任何一個數目就是了。

指南119 用屬性型別來替代關係線(Replace Relationship Lines with Attribute Types)
並不是所有的概念都要以類別或關係線的方式表達,不是領域特殊的概念其實可以考慮用屬性型別的方式來表達。例如圖9的範例中,會員和訂購交易都跟地址有關。不過,地址並非領域特殊概念,它屬於一般性概念,而且根本是眾所皆知的概念,此時其實可以改用圖10的屬性型別的方式來表達,簡化類別圖的複雜度。



指南120 別建立隱含關係(Do Not Model Implied Relationships)
隱含關係不僅會讓圖面雜亂,還會將風險轉移到維護這類關係的開發人員身上,所以別建立隱含關係。以圖11為例,每一筆訂購都由數個小的訂購次項所組成,而每一個訂購次項又會關連到一個特定的商品,因此訂購與商品之間其實存在有隱含關係。


而依照編號120指南的建議是,即便訂購與商品之間存在有隱含關係,也不宜在圖面上建立出這條隱含關係。換言之,圖11的設計比較合宜,不建議繪製成圖12的樣子。


指南121 不宜建立出所有的依賴關係(Do Not Model Every Dependency)
除非有加強溝通的價值,否則不宜於類別圖中顯示出所有的依賴關係。由於,物件之間有許多情況都具有依賴關係,全部繪製出來只會讓類別圖面變得雜亂無章,所以,我在實務上就幾乎不在類別圖中繪製這種用完即丟的暫時性關係。結合關係
「結合關係」(association relationship)是一種需要長期保存的靜態關係,它是類別圖中最重要且常見的關係。在本小節中,編號從122到133,一共十二條指南,都與結合關係有關。

指南122 結合名稱置中(Center Names on Associations)
我們可以在結合關係線的旁邊放置名稱,用來標示出這條結合關係的名稱,如圖13所示,指南則建議將結合名稱放置於結合關係線的中央位置。


指南123 使用主動語態的動詞來為結合關係命名(Write Concise Association Names in Active Voice)

指南124 使用方向標記讓結合名稱更清楚(Indicate Directionality to Clarify an Association Name)

指南125 讓單向結合的名稱和它的方向同向(Name Unidirectional Associations in the Same Direction)

指南126 結合名稱由左唸到右(Word Association Names Left to Right)
主動語態的動詞比被動語態清楚且易懂,如果再配合閱讀的方向標記,那就更好了。而且,由於西式的書寫及閱讀習慣是由左到右,所以最好方向標記也可以是指向右方的箭頭,像圖14的範例中,我們就可以很清楚且通順地唸出「投資人購買股票」,如果是圖15的表達,唸起來就極為不順暢。



再者,如果是單向結合的話,最好結合名稱的閱讀方向可以跟單向結合同方向,如圖16所示。


指南127 兩個類別之間存在多個結合關係時,標示出角色名稱(Indicate Role Names When Multiple Associations Between Two Classes Exist)
我們可以在結合關係的兩端點處設置「角色」(role),用來說明該類別以什麼樣的角色來參與這項結合關係的。通常,如果兩類別之間只有一條結合關係時,我們經常會省略結合名稱,也不會特別標示角色名稱。

不過,如果兩類別之間存在多條不同的結合關係時,我們就會為這些結合關係命名,或者標示角色名稱,以便區分這些結合關係,如圖17所示。


指南128 在反身結合關係上,標示出角色名稱(Indicate Role Names on Recursive Associations)
結合關係的兩端點可以連到同一個類別嗎?當然可以,這種情況稱為「反身結合」(reflexive association)。不過,所謂反身結合並不是指連結線的兩端,連結到相同的物件,而是連結到同類別所產出的不同物件。

請看圖18的例子,反身結合很適合表達商品套件的情況。比方說,麥當勞賣的商品裡頭,有些是單品,有些是套餐。一個套餐裡頭通常包含了多項單品,不過無論是套餐或單品,都是屬於可以販賣的商品,所以僅設計一個商品類別。
在反身結合的例子裡,很需要使用角色名稱及結合名稱,來協助理解結合關係,否則不容易看懂。如果我們將圖18改成圖19,是不是比較容易理解。


指南129 當兩個方向上都會有合作發生時,才建立雙向的結合關係(Make Associations Bidirectional Only When Collaboration Occurs in Both Directions)
雙向結合關係的開發和維護成本都較高,所以只有當需要雙向航行時,才會建立雙向的結合關係,否則使用單向結合,成本較低廉。

以圖20為例,我們需要從投資人正向連到他所擁有的密碼,但不需要反向從某一個密碼查到它的擁有人是誰,所以維護單向的結合關係即可。但是,投資人和帳戶之間的關係則需要維護其雙向性,因為我們會需要從某一個帳戶反向查出哪位投資人擁有該帳戶。


指南130 只有在單向結合關係上,才標示出方向性(Indicate Direction Only on Unidirectional Associations)

指南131 避免標示出不可航性(Avoid Indicating Non-Navigability)
特別注意的是,結合點端的標記在新版的UML2版中有所變動,與UML1版略有不同。請看到圖21的範例,這是UML規格書上的範例,我重新繪製用來說明此處的概念。先談UML2版,結合關係的箭頭端,代表具有可航性;打個小叉叉,就是不可航;單純直線,代表還未指定可航或不可航。


所以,再回頭看到圖21的例子中:

● AB是雙向結合。
● CD是兩端都不具有可航性。
● EF還未指定兩端是否具可航性,還是不具有可航性。
● GH為單向結合,可由G航行到H。
● I端還未決定可否航行,可以確定的是,可由I航向J。

UML2版這樣定義之後,其實就很清楚了。不過,由於UML1版不是這樣定義的,所以實務上混淆著UML1版和UML2版的情況下,就容易造成誤解。

我們來看原先UML1版的定義,只有兩種情況:有箭頭或沒箭頭。有箭頭就是可航行,沒箭頭就不可航行。所以,如果以UML1版來解釋圖21的話:

● AB是雙向結合。
● CD無法解釋,小叉叉是UML2版的新符號。
● EF也是雙向結合。
● G端無法解釋,H端具有可航性。
● IJ是單向結合,由I端航行到J端。

當年UML1版給了兩套表示法,只是實務上都採用第一套表示法:

1.直線代表雙向結合;單箭頭代表單向結合。

2.雙箭頭代表雙向結合;單箭頭代表單向結合。

回到實務上來說,畫雙箭頭或小叉叉太繁瑣,所以現在都還是沿用單箭頭表示單向結合,直線表示雙向結合的表示法。到目前為止,多數的UML工具都還是採用直線代表雙向結合、單箭頭代表單向結合,也因此,指南130和指南131才會建議不用雙箭頭、也不用小叉叉標記。

指南132 只有在有改變時,才重新繪製繼承而來的結合關係(Redraw Inherited Associations Only When Something Changes)
談到一般化關係所帶來的繼承效果,許多人都只知道屬性與操作的繼承,忽略了結合關係也可以被繼承下來。比方說,在圖22範例中,個人父類別與密碼類別之間的結合關係,就可以透過一般化關係繼承給存戶子類別與投資人子類別,繼承下來的情況如圖23所示。



通常,不需要特別繪製出繼承下來的子結合,除非是繼承而來的子結合有所改變,這時便如指南132所建議的,需要把子結合繪製出來。延續上述的範例,如果銀行規定投資人需要設置兩組密碼,比一般的存戶多一組密碼,如圖24所示。在這種情況下,就需要將子結合繪製出來,以便能夠更改個體數目,如圖25所示。



其實,結合關係之間也可以擺放一般化關係,就跟類別之間的一般化關係圖示一樣,如圖26所示,只不過,大多數的UML工具都沒有支援這樣的表達方式。


指南133 詢問個體數目的上下限(Question Multiplicities Involving Minimums and Maximums)
其實,個體數目在反映企業規則,所以它很可能會隨著時間而變動,必須對它保持開放性的詢問。

比方說,在上述的銀行範例中,過去銀行規定,無論是存戶或投資人只需要設置一組密碼。可是後來,因為提高了安全機制,所以更動了原先的企業規則,在新的企業規定中,投資人必須比存戶多設置一組密碼,變成設置兩組密碼才可以。繼承關係
前面我們談過的「一般化關係」(generalization relationship),也就是此處的「繼承關係」(inheritance relationship),從編號134到139一共有六條指南跟一般化關係有關。

指南134 把語句規則應用在繼承關係上(Apply the Sentence Rule for Inheritance)
繼承關係的語句規則是指「子類別是父類別(A subclass IS A superclass)」,或者「子類別是一種父類別(A subclass IS KIND OF A superclass)」。

例如,光學滑鼠、滾輪滑鼠與滑鼠這三者之間的關係,我們會說「光學滑鼠是滑鼠」或者說「滾輪滑鼠是一種滑鼠」,因此它們之間有一般化關係,如圖27所示。


如果無法通過這個語句規則的話,通常不適合使用一般化關係。比方說,很多手機都具備有mp3音樂播放功能,現在就連我手頭上的英文翻譯機都可以播放mp3音樂。可是很少人會說「英文翻譯機是mp3播放器」或者說「手機是一種mp3播放器」,比較有可能會說「英文翻譯機和手機都具備有播放mp3音樂的功能」,所以圖28便是一種誤用一般化關係的範例了。


指南135 把子類別放置於父類別的下方(Place Subclasses Below Superclasses)
老話一句,善用格線底稿,可以讓圖面看起來整齊劃一,如圖29所示。


指南136 謹防以資料為基礎的繼承關係(Beware of Data-Based Inheritance)

指南137 子類別應該繼承一切(A Subclass Should Inherit Everything)
如果,子類別只想繼承父類別的屬性,而不繼承其餘的操作和關係的話,這種以資料為基礎的繼承關係恐怕需要再審視。

最好子類別可以繼承父類別的一切,包含屬性、操作及關係,這種繼承一切的情況又稱為「純粹繼承」(pure inheritance)。

純粹繼承有個好處是,我們只需要了解子類別繼承了什麼,而不需要知道子類別有什麼不繼承。

乍聽之下,你可能會覺得這沒什麼,可是對於擁有多層次的複雜繼承架構而言,這其實是很有價值的。譬如,UML本身的靜態結構正是因為採用了純粹繼承的設計風格,所以大幅降低了它的複雜度。

指南138 結合之間存有一般化關係時,區別之(Differentiate Generalizations Between Associations)
前面我們已經談過,結合關係之間也可以擁有一般化關係,雖然這種範例比較少見。

一旦,結合關係之間有一般化關係時,最好可以明顯地區別之,比方說用不同的顏色來表達父結合與子結合,如圖30所示。


指南138 在共有繼承關係中,標示出「超型別」(Indicate Power Types on Shared Generalization)
「超型別」(power type)的概念在實務上也是少用,它是UML高級認證的範圍之一。

簡單來說,超型別是一種特殊的類別,它的個體是別的類別的子類別,也因此,超型別的概念通常會跟著一般化關係一塊出現。

請看圖31的範例,在這個範例中,人可以依照「性別」分為男人和女人,也可以依照「法定年齡」分為成年人和未成年人,此處的性別和法定年齡就是超型別。


結合是一種平等關係,但是有時我們可能會需要表達,整體-部分(whole-part)這種一大一小或一重一輕的不平等關係。

所以,UML以結合關係為基礎,延伸出「聚合關係」(aggregation association)與「組合關係」(composition aggregation),下期將會介紹這些指南,同時也開始進入循序圖的指南。

作者簡介:
邱郁惠
研究OOAD、UML、MDA十餘年,經歷過顧問、專案、教學及寫作工作。離職後創辦UML Blog推廣UML,組織《UML互助會》社群定期舉辦軟體技術講座,出版多本UML專業書籍與電子書。目前擁有OCUP/UML三級認證、PMP認證。

相關連結
沒時間讀 UML/OOAD 書之挑讀筆記 第10回 UML類別圖的風格指南(上))

熱門新聞

Advertisement