天天看點

Java 繼承、依賴、關聯、組合、聚合

類之間的關系大體上存在五種:繼承(實作)、依賴、關聯、聚合、組合。

這其中聚合群組合都是關聯的一種特列。

繼承:對于類來說,這種關系叫做繼承,而對于接口來說,這種關系叫做實作。繼承是一種“is-a”關系。

依賴:簡單的了解,就是一個類A中的方法使用到了另一個類B。這種使用關系是具有偶然性的、臨時性的、非常弱的,但是B類的變化會影響到類A。

比如說,我想打籃球,首先需要一個類來代表我自己,然後需要一個類來代表一一個籃球,最後,‘我’要調用‘籃球’裡的方法來玩耍,用代碼實作一下:

public class Ball {
    public void play(){
        System.out.println("use ball to play");
    }
}
 
public class Me {
    public void play(Ball ball){//這裡,play作為Me類方法的參數。  Me類依賴Ball類
        ball.play();
    }
}
           

這就是一種類與類之間的關系,叫做依賴。

一般而言,依賴關系在Java中展現為局域變量、方法的形參,或者對靜态方法的調用。

關聯:被關聯的類以類屬性的形式出現在關聯的類中,或者關聯類A引用了一個類型為被關聯類B的全局變量的這種關系,就叫關聯關系。

展現的是兩個類、或者類與接口之間語義級别的一種強依賴關系。這種關系比依賴更強、不存在依賴關系的偶然性、關系也不是臨時性的,一般是長期性的,而且雙方的關系一般是平等的、關聯可以是單向、雙向的。

// Ball還是上面的Ball
public class You {
    private Ball ball; // 讓Ball成為You的類屬性,這就是關聯

    public You(Ball ball){
        this.ball = ball;
    }

    public void play(){
        ball.play();
    }
}
           

在Java中,關聯關系一般使用成員變量來實作。

聚合:是關聯關系的一種特例,他展現的是整體與部分、擁有的關系,整體與部分是可分的,即has-a的關系

public class Family {
    private List<Child> children; //一個家庭裡有許多孩子

    // ...
}
           

在代碼層面,聚合和關聯關系是一緻的,隻能從語義級别來區分。普通的關聯關系中,a類和b類沒有必然的聯系,而聚合中,需要b類是a類的一部分,是一種”has-a“的關系,即 a has-a b; 比如家庭有孩子,屋子裡有空調。但是,has 不是 must has,a可以有b,也可以沒有。

不同于關聯關系的平等地位,聚合關系中兩個類的地位是不平等。

組合:是關聯關系的一種特例,他展現的是一種contains-a的關系,整體與部分是不可分的,關系比聚合更強,也稱為強聚合。

public class Person {
    private Eye eye = new Eye();  //一個人有鼻子有眼睛

    // .... 
}
           

組合同樣展現整體與部分間的關系,但此時整體與部分是不可分的,整體的生命周期結束也就意味着部分的生命周期結束。

同樣,組合關系中,兩個類關系也是不平等的。

幾種關系所表現的強弱程度依次為:組合>聚合>關聯>依賴;

總結

學過設計模式的都知道,要“少用繼承,多用組合”,這究竟是為什麼呢?

組合與繼承的差別和聯系

在繼承結構中,父類的内部細節對于子類是可見的。是以我們通常也可以說通過繼承的代碼複用是一種 白盒式代碼複用。(如果基類的實作發生改變,那麼派生類的實作也将随之改變。這樣就導緻了子類行為的不可預知性)

組合是通過對現有的對象進行拼裝(組合)産生新的、更複雜的功能。因為在對象之間,各自的内部細節是不可見的,是以我們也說這種方式的代碼複用是黑盒式代碼複用 。(因為組合中一般都定義一個類型,是以在編譯期根本不知道具體會調用哪個實作類的方法)

繼承在寫代碼的時候就要指名具體繼承哪個類,是以,在編譯期就确定了關系。

組合,在寫代碼的時候可以采用面向接口程式設計。是以,類的組合關系一般在運作期确定。

組合是在組合類和被包含類之間的一種松耦合關系,而繼承則是父類和子類之間的一種緊耦合關系。

當選擇使用組合關系時,在組合類中包含了外部類的對象,組合類可以調用外部類必須的方法,而使用繼承關系時,父類的所有方法和變量都被子類無條件繼承,子類不能選擇。

引用網友的一句很經典的話應該更能讓大家厘清繼承群組合的差別:組合可以被說成“我請了個老頭在我家裡幹活” ,繼承則是“我父親在家裡幫我幹活”。從這句話可以看出組合的靈活性比繼承高,這應該是“少用繼承,多用組合”的原因吧。

是以建議在同樣可行的情況下,優先使用組合而不是繼承。