c 如何用組合替代繼承

2022-09-26 01:27:10 字數 1825 閱讀 1827

如果問物件導向的三大特性是什麼,多數人都能回答出來:封裝、繼承、多型。

繼承 作為三大特性之一,近來卻越來越不推薦使用,更有極端的語言,直接語法中就不支援繼承,例如 go。這又是為什麼呢?

假設我們要設計乙個關於鳥的類。

我們將「鳥類」定義為乙個抽象類abstractbird。所有更細分的鳥,比如麻雀、鴿子、烏鴉等,都繼承這個抽象類。

大部分鳥都會飛,那我們可不可以在abstractbird抽象類中,定義乙個fly()方法呢?

答案是否定的。儘管大部分鳥都會飛,但也有特例,比如鴕鳥就不會飛。鴕鳥繼承具有fly()方法的父類,那鴕鳥就具有「飛」這樣的行為,這顯然不符合我們對現實世界中事物的認識。

在鴕鳥這個子類中重寫fly()方法,讓它丟擲異常。

public class abstractbird

}//鴕鳥

public class ostrich : abstractbird

}這種設計思路雖然可以解決問題,但不夠yqbtvpaiu優美。因為除了鴕鳥之外,不會飛的鳥還有很多,比如企鵝。對於這些不會飛的鳥來說,我們都需要重寫fly()方法,丟擲異常。

這違背了迪公尺特法則(也叫最少知識原則),暴露不該暴露的介面給外部,增加了類使用過程中被誤用的概率。

通過abstractbird類派生出兩個更加細分的抽象類:會飛的鳥類 程式設計客棧abstractflyablebird和不會飛的鳥類abstractunflyablebird,讓麻雀、烏鴉這些會飛的鳥都繼承abstractflyablebird,讓鴕鳥、企鵝這些不會飛的鳥,都繼承abstractunflyablebird類。

此時,繼承關係變成了三層,還行得通。

如果要再新增乙個游泳swim()的方法,那情況就複雜了,要分為四中情況:

如果再有其他行為加入,抽象類的數量就會幾何級數增長。

我們要www.cppcns.com搞清楚某個類具有哪些方法、屬性,必須閱讀父類的**、父類的父類的**……一直追溯到最頂層父類的**。

針對「會飛」這樣乙個行為特性,我們可以定義乙個flyable介面,只讓會飛的鳥去實現這個介面。針對會游泳,定義乙個swimable介面,會叫定義乙個tweetable介面。

public inte***ce flyable

public inte***ce swimable

public inte***ce tweetable

//麻雀

public class sparrow : flyable, tweetable

//企鵝

public class penguin : swimable, tweetable

麻雀和企鵝都會叫,tweet實現了兩遍,這是壞味道。我們可以用組合來消除這個壞味道。

public inte***ce flyable

public inte***ce swimable

public inte***ce tweetable

public class flyability : flyable

public class swimability : swimable

public class tweetability : tweetable

//麻雀

public class sparrow : flyable, tweetable

//企鵝

public class penguin : swimable, tweetable

雖然現在主流的思想都是多用組合少用繼承,但是從上面的例子可以看出,繼承改寫成組合意味著要做更細粒度的類的拆分,要定義更多的類和介面。類和介面的增多也就或多或少地增加**的複雜程度和維護成本。所以,在實際的專案開發中,我們還是要根據具體的情況,來具體選擇該用繼承還是組合。

如何用查表替代運算

對於sin,cos,tan等三角函式來說,使用cpu fpu計算所花費的時間比加減法員算要慢很多的時間 這就使得在很多密集的三角運算花費了大量的時間,如何在保證一定精度的情況下,使用近似的方法來獲取這些函式的數值呢 常規的做法,就是犧牲空間換取時間的查表法 需要注意的是,查表法不是萬能的,必須針對特...

c 繼承,組合

1 什麼是繼承 a繼承b,說明a是b的一種,並且b的所有行為對a都有意義 eg a woman b human a 鴕鳥 b 鳥 不行 因為鳥會飛,但是鴕鳥不會。2.什麼是組合 若在邏輯上a是b的 一部分 a part of 則不允許b從a派生,而是要用a和其它東西組合出b。例如眼 eye 鼻 no...

c 繼承和組合

當建立乙個物件時,編譯器總是確保呼叫了所有的子物件的建構函式,如果子物件有自己的預設建構函式,那麼編譯器可以自動地呼叫它們。但是,如果子物件沒有預設建構函式,或者想改變建構函式的某個預設引數,這就會出現問題,因為這個新類的建構函式沒有權利訪問這個子物件的私有資料成員,所有不能直接對它們初始化。解決的...