天天看點

繼承與複合的選擇

繼承與複合

繼承與複合都可以在一定程度上對類進行擴充。

繼承

假設,B類繼承自A類,B類是A類的派生類,子類B具有A類的某些特性。那麼可以說,A類和B類是同一種東西,也就能使用is-a來表示兩者的關系-----繼承。

繼承分為接口繼承和實作繼承,兩者主要的目标是代碼重用。

舉個例子

車具有引擎和車輪,奔馳車也具有引擎和車輪,同時奔馳車具備基礎車不具備的Boss音響,即便如此,它仍然是車,因為它具備基礎車所有的屬性,隻是在基礎車上增加了Boss音響。用英語表示兩者的關系是:BenzCar is Car.

使用XML圖表示兩者的關系:

繼承與複合的選擇
@Data
public class Car {
    /**
     * 引擎
     */
    private String engine;
    /**
     * 輪子
     */
    private String wheel;
}           

複制

public class BenzCar extends Car{
    private String bossSound;
}           

複制

複合

假設,B類需要引用A類的某些屬性和功能,B類隻需要A類的屬性,并不需要暴露/知道A類的具體細節,當需要擷取A類的屬性或資料時,僅需要暴露A類的API提供通路就可以,我們認為,A類和B類不是同一種東西,B類隻是需要A類的部分資訊,也就能使用has-a來表示兩者的關系-----複合。

B類的任何一個執行個體對象都可以調用現有方法來傳回A類的結果,這種方式叫轉發。

同樣舉個例子

車庫裡有各種各樣的燈和不同的車,車庫并不關心車庫内有什麼品牌的車,車本質上并不是車庫的一部分,而是隻需要實作停車的功能或者需要燈具實作照亮車庫的功能,那麼我們隻需要在車庫中包含車輛、燈具的執行個體就可以實作。

使用XML圖表示兩者的關系:

繼承與複合的選擇
@Data
public class Opple {
    /**
     * 燈
     */
    private String light;
}           

複制

@Data
public class YeeLight {
    /**
     * 燈
     */
    private String light;
}           

複制

@Data
public class ParkLot {
    /**
     * 停車場需要歐普的燈實作照亮功能
     */
    private Opple opple;
    /**
     * 停車場需要yeeLight的燈實作照亮功能
     */
    private YeeLight yeeLight;
    /**
     * 停車場裡有車,實作停車功能
     */
    private Car car;
}           

複制

Tips

無論繼承還是複合,兩者實質上都是對原有類進行擴充,隻不過是方式不同、适用場景不同,兩者XML合在一起長這樣:

繼承與複合的選擇

但是,我們在擴充類時

需要考慮的點:兩者是is-a是與否的關系還是has-a包含關系?

需要遵循的原則是:複合優先于繼承。

需要明确的點是:繼承破壞了封裝性,子類依賴于父類特定的功能和細節,但是父類在疊代中,子類可能會遭到破壞,可能導緻子類出現壞結果。