繼承與複合
繼承與複合都可以在一定程度上對類進行擴充。
繼承
假設,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包含關系?
需要遵循的原則是:複合優先于繼承。
需要明确的點是:繼承破壞了封裝性,子類依賴于父類特定的功能和細節,但是父類在疊代中,子類可能會遭到破壞,可能導緻子類出現壞結果。