3. 父類引用子類
這是說一個(gè)父類保存了其子類的引用,返回具體的其子類類型,以具體的其子類類型做為方法的參數(shù),或者在方法中以本地變量的方式使用、初始化了具體的其子類實(shí)例。一句話,父類的任何地方都不應(yīng)該出現(xiàn)“以其子類型”定義的靜態(tài)變量、實(shí)例變量和本地變量(Local variables)。
組合關(guān)系
父類組合(Composite)其子類的關(guān)系不能存在,因?yàn)檫@會(huì)導(dǎo)致運(yùn)行時(shí)刻的遞歸初始化,產(chǎn)生StackOverflow錯(cuò)誤,代碼如下:
public class Base {
Derived d = new Derived();
// 或者在構(gòu)造方法中調(diào)用new Derived();
public Base() {
d = new Derived();
}
}
public class Derived extends Base {
}
只要存在父類組合其子類的關(guān)系,即要在父類初始化時(shí)刻同時(shí)初始化其子類,必定導(dǎo)致這樣的問題,無論是在定義實(shí)例變量的時(shí)候初始化,還是在構(gòu)造器中初始化。當(dāng)然,即使實(shí)例變量定義為父類,如果實(shí)際初始化的是其子類,也會(huì)產(chǎn)生同樣的錯(cuò)誤。
聚合關(guān)系
父類聚合(Aggregate)其子類的關(guān)系也不應(yīng)該存在。聚合關(guān)系和組合關(guān)系的不同就在于初始化過程,在聚合關(guān)系中,其子類的初始化不在父類初始化過程中,這不會(huì)導(dǎo)致遞歸問題。但這并不是說這樣做就是好的。
直接以其子類型定義靜態(tài)變量、實(shí)例變量,等于把抽象的父類綁定到了具體的子類型。這是對“繼承關(guān)系是一種抽象關(guān)系”的破壞,它使更抽象的父類不再具有抽象的涵義。
在父類方法的實(shí)現(xiàn)中,以其子類型定義本地變量,和父類聚合其子類“效果”是一樣的。
以父類型或者更抽象的類型來定義變量
在父類中,可以使用父類型或者更抽象的類型來定義變量。雖然不是一種很好的實(shí)踐,但有時(shí)的確需要把其子類型的實(shí)例付值給這些變量。一般情況下,這樣做的時(shí)候會(huì)選擇動(dòng)態(tài)加載子類的實(shí)例:
public abstract class Factory {
private static Factory instance;
public synchronized static Factory getInstance() {
// 從配置文件中讀取子類型名稱
String factoryName = readFromConfig();
instance = Class.forName(factoryName).newInstance();
....
}
public Object createSomething();
}
即使如此,我個(gè)人的看法是,盡可能避免使用子類的實(shí)例。
這是說一個(gè)父類保存了其子類的引用,返回具體的其子類類型,以具體的其子類類型做為方法的參數(shù),或者在方法中以本地變量的方式使用、初始化了具體的其子類實(shí)例。一句話,父類的任何地方都不應(yīng)該出現(xiàn)“以其子類型”定義的靜態(tài)變量、實(shí)例變量和本地變量(Local variables)。
組合關(guān)系
父類組合(Composite)其子類的關(guān)系不能存在,因?yàn)檫@會(huì)導(dǎo)致運(yùn)行時(shí)刻的遞歸初始化,產(chǎn)生StackOverflow錯(cuò)誤,代碼如下:
public class Base {
Derived d = new Derived();
// 或者在構(gòu)造方法中調(diào)用new Derived();
public Base() {
d = new Derived();
}
}
public class Derived extends Base {
}
只要存在父類組合其子類的關(guān)系,即要在父類初始化時(shí)刻同時(shí)初始化其子類,必定導(dǎo)致這樣的問題,無論是在定義實(shí)例變量的時(shí)候初始化,還是在構(gòu)造器中初始化。當(dāng)然,即使實(shí)例變量定義為父類,如果實(shí)際初始化的是其子類,也會(huì)產(chǎn)生同樣的錯(cuò)誤。
聚合關(guān)系
父類聚合(Aggregate)其子類的關(guān)系也不應(yīng)該存在。聚合關(guān)系和組合關(guān)系的不同就在于初始化過程,在聚合關(guān)系中,其子類的初始化不在父類初始化過程中,這不會(huì)導(dǎo)致遞歸問題。但這并不是說這樣做就是好的。
直接以其子類型定義靜態(tài)變量、實(shí)例變量,等于把抽象的父類綁定到了具體的子類型。這是對“繼承關(guān)系是一種抽象關(guān)系”的破壞,它使更抽象的父類不再具有抽象的涵義。
在父類方法的實(shí)現(xiàn)中,以其子類型定義本地變量,和父類聚合其子類“效果”是一樣的。
以父類型或者更抽象的類型來定義變量
在父類中,可以使用父類型或者更抽象的類型來定義變量。雖然不是一種很好的實(shí)踐,但有時(shí)的確需要把其子類型的實(shí)例付值給這些變量。一般情況下,這樣做的時(shí)候會(huì)選擇動(dòng)態(tài)加載子類的實(shí)例:
public abstract class Factory {
private static Factory instance;
public synchronized static Factory getInstance() {
// 從配置文件中讀取子類型名稱
String factoryName = readFromConfig();
instance = Class.forName(factoryName).newInstance();
....
}
public Object createSomething();
}
即使如此,我個(gè)人的看法是,盡可能避免使用子類的實(shí)例。