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.