標籤:method 地區 編寫 屬性 nal 分享 files 區別 存取修飾詞
建議103:區分組合和繼承的應用場合
繼承所帶來的多態性雖然是物件導向的一個重要特性,但這種特性不能在所有的場合中濫用。繼承應該被當做設計架構的有用補充,而不是全部。
組合不能用於多態,但組合使用的頻率卻要遠遠高於繼承。
繼承UML圖如下:
對應的代碼如下:
abstract class Stream { //省略 } class FileStream:Stream { //省略 } class MemoryStream : Stream { //省略 }
組合UML圖如下:
對應代碼如下:
class Context { //省略 } class CultureInfo { //省略 } class Thread { private Context m_Context; private CultureInfo m_CultureInfo; //省略 }
從設計的角度來看,繼承代表的是“Is a”,組合代表的是“Has a”。FileStream和MemoryStream都是(Is a)Stream,而對於線程Thread來說,它擁有(Has a)線程上下文Context和地區資訊CultureInfo。這是最重要的區別,任何時候,設計理念上的因素總是排在第一位。
繼承不僅僅是指繼承自某個類型(class),也可以指繼承自某個介面(interface)。繼承最大的優點就是多態,這也奠定了面向抽象編程的基礎。繼承提高了代碼的複用性。組合顯然不具備這種特性。
從文法角度來看,繼承易於擴充。基類一旦擴充一個具有public、internal、protected存取修飾詞的介面,所有的子類都會自動擁有其介面,組合則不能。組合要擁有任何對象的行為,必須手動編碼。以Thread為例,組合要擁有CultureInfo的行為,必須首先在自身內部包含一個CultureInfo欄位,如下:
class CultureInfo { //省略 public void OtherMethod() { } } class Thread { private Context m_Context; private CultureInfo m_CultureInfo; //省略 public void OtherMethod() { m_CultureInfo.OtherMethod(); } }
到目前為止,似乎一直在說繼承的優點。事實上,繼承的以上優點,正好又是它的缺點。子類天然具有基類的公開介面,而這正好破換了物件導向的“封裝性”。我們顯然不需要每一層的類型都具有上層的所有介面。一個類,如果其繼承體系達到3層(當然,凡事都有例外,WPF體系中的控制項整合體系,以Shape為例,多達7層),就可以考慮停止了。如果不停止,對調用者來說,最底層的類型會有多少公開的方法和屬性呢?答案是最底層的類型會擁有所有上層類型的開放介面。隨著項目的發展,組合的優勢會逐漸體現出來,它良好的封裝性使類型可以對外宣布:我只做一件事。
組合的另一個優勢是,它可以組合多個其他類型。如果組合太多的類型,就意味著當前的類很可能做了太多的事情,它就需要拆分成兩個類了。繼承不具有這樣的特性,在C#中,子類只能有一個基類(介面則放開這種限制,子類可以繼承自多個介面)。
應當根據實際情況考慮是使用繼承還是組合。一般來講,組合更能滿足大部分的應用情境。不要為了讓代碼看起來像“物件導向”,而濫用繼承。
轉自:《編寫高品質代碼改善C#程式的157個建議》陸敏技
【轉】編寫高品質代碼改善C#程式的157個建議——建議103:區分組合和繼承的應用場合