has-a
has-a(英語:has_a、has a)是物件組合的關係[註 1],這是一種以組合為概念的關係[註 2]。
主要是用在資料庫設計與面向對象程序設計以及面向對象的系統分析的程式設計領域,就是一個物件(部件/組合成分/成員)「屬於」另一個物件(複合型態),而且是根據物件的所有權規則來執行動作。這個組合的關係也就是一個物件成為另一個物件的「一部分」,例如物件A成為物件B的一部分也就是物件B的成員。
簡單來說,has-a關係對於物件來說就是物件的成員變量,或是成員物件。多個has-a關係會組成產生一個獨有性階層結構。
相關概念
分類性階層(子類型)的is-a關係與has-a是相互對比的概念[註 3]。存在明確的is-a關係才較為適合使用繼承方法。繼承的問題在於各項物件緊密連接而難以修改,還有過度使用會導致混亂的階層結構。以組合的概念來說,只要組合簡單物件即可達成複雜的物件[1]。
不論是has-a或is-a關係,上層與下層物件兩者大多的邏輯關係通常都不是很明確。對於邏輯關係的混亂需要元語言學專用詞來做決定。標準模板庫的容器是has-a關係的最佳範例。
以元語言學總結各種關係如下:
- 上位詞—下位詞(父型態—子型態)關係介於定義在分類性階層結構的型態(類別)之間:
- 對於繼承關係:對於上位詞(父型態、父類別)而言,下位詞(子型態、子類別)有型態(is-a)關係[註 4]。
- 整體詞—分體詞(完整體/實體/容器部件/成分/成員)關係介於定義在獨有性階層結構的型態(類別)之間:
- 概念—物件(型態—代幣)關係介於型態(類別)與物件(實例)[註 7]:
- 對於型態(類別)而言,代幣(物件)有實例關係[註 8]。
範例
ER模型
資料庫的has-a關係通常以ER模型來表示。
如圖所示,以大型多人在線角色扮演遊戲為例,一個遊戲帳號可以建立多個遊戲角色。這表示對於遊戲角色而言[註 9],遊戲帳號有has-a關係[註 10]。
UML類別圖
統一建模語言的類別圖可以用來表示物件導向程式設計的has-a關係,也就是組合關係。
如圖所示,具有菱形符號貼合的物件是由其他物件組成(或是包含其他物件):
- 組合關係:對於化油器而言汽車有has-a關係,或者汽車是由化油器組成的,汽車物件右方的黑色菱形符號表示組合關係。
- 聚合關係:池塘與鴨子兩者用白色菱形符號來表示聚合關係,池塘包含鴨子但是無所有權,因為鴨子可以自行單獨活動甚至主動離開池塘。
也就是說,組合關係表示具備所有權,聚合關係表示無所有權。
C++
試圖以程序設計塑模現實世界的事物時,區分組合與聚合兩者的另一種方法是考慮成員物件(被包含的物件)的生命週期。
例如,用汽車來表示一個物件,汽車包含底盤這個物件,對於汽車的生命週期來說底盤的替換性很低,或是極不可能更換。因為底盤的生命週期相當於汽車本身的壽命,所以底盤與汽車是一種組合關係。另一方面,如果汽車包含輪胎物件,輪胎有可能耗損而需要更換。或者是汽車無法使用,但是輪胎堪用還能繼續使用甚至回收分配給另一輛汽車。不管發生什麼情形,輪胎的生命週期不同於汽車本身,所以輪胎與汽車是一種聚合關係。
用C++類別實作上述範例的關係:
- 組合關係:汽車要包含完整的底盤物件也就是把底盤宣告為成員變數。底盤物件只能在汽車類別的建構子來實例化而且成為汽車類別的成員變數之一(或者是定義為成員變數的資料型態再到建構子內設定屬性), 底盤物件的生命週期會隨著汽車物件被刪除而終止。
- 聚合關係:汽車類別的成員變數通常是用指標來指向輪胎物件。輪胎物件可以在汽車物件的外部來實例化或是被刪除,或是指定給其他不同的汽車物件。輪胎物件有獨立的生命週期,汽車物件被刪除時輪胎物件依舊還存在,輪胎可以從汽車分離出來。
也就是說,對於汽車類別而言,底盤物件是汽車的成員變數(組合關係),輪胎物件是汽車的指標變數(聚合關係)。
註釋
參考資料
- ^ Cohen, Sheldon. 優先選用組合而非繼承(Favoring Composition Over Inheritance). Medium. 2023-05-19.