類繼承和對象組合是代碼復用的兩種最常用的技術
一、繼承
繼承是 Is-a 的關系,比如說Student繼承Person,則說明Student is a Person。繼承的優點是子類可以重寫父類的方法來方便地實現對父類的擴展。
繼承的優點有:
1、容易進行新的實現,因為其大多數可繼承而來;
2、 易於修改或擴展那些被復用的實現;
繼承的缺點有:
1、父類的內部細節對子類是可見的;
2、子類從父類繼承的方法在編譯時就確定下來了,所以無法在運行期間改變從父類繼承的方法的行為;
3、如果對父類的方法做了修改的話(比如增加了一個參數),則子類的方法必須做出相應的修改。所以說子類與父類是一種高耦合,違背了面向對象思想。
二、組合
組合是 has-a 的關系,組合就是設計類的時候把要組合的類的對象加入到該類中作為自己的成員變量。
組合的優點:
1、當前對象只能通過所包含的那個對象去調用其方法,所以所包含的對象的內部細節對當前對象時不可見的;
2、當前對象與包含的對象是一個低耦合關系,如果修改包含對象的類中代碼不需要修改當前對象類的代碼;
3、當前對象可以在運行時動態的綁定所包含的對象。可以通過set方法給所包含對象賦值。
組合的缺點:
1、容易產生過多的對象;
2、為了能組合多個對象,必須仔細對接口進行定義。
由此可見,組合比繼承更具靈活性和穩定性,所以在設計的時候優先使用組合。只有當下列條件滿足時才考慮使用繼承:
1、子類是一種特殊的類型(String類、Object類、包裝類),而不只是父類的一個角色;
2、子類的實例不需要變成另一個類的對象;
3、子類是父類的擴展,而不是覆蓋或者使父類的功能失效;
為什么使用組合關系?
類繼承允許我們根據自己的實現來覆蓋重寫父類的實現細節,父類的實現對於子類是可見的,我們一般稱之為白盒復用。對象持有(其實就是組合)要求建立一個號的接口,但是整體類和部分類之間不會去關心各自的實現細節,即它們之間的實現細節是不可見的,故成為黑盒復用。
繼承是在編譯時刻靜態定義的,即是靜態復用,在編譯后子類和父類的關系就已經確定了。而組合這是運用於復雜的設計,它們之間的關系是在運行時候才確定的,即在對對象沒有創建運行前,整體類是不會知道自己將持有特定接口下的那個實現類。在擴展方面組合比集成更具有廣泛性。
繼承中父類定義了子類的部分實現,而子類中又會重寫這些實現,修改父類的實現,在設計模式中認為這是一種破壞了父類的封裝性的表現。這個結構導致的結果是父類實現的任何變化,必然導致子類的改變。然而組合這不會出現這種現象。對象的組合就是有助於保持每個類被封裝,並被集中在單個任務上(符合類設計的單一原則)。這樣類的層次結構不會擴大,一般不會出現不可控的龐然大類。而類的繼承就可能出來這些問題,所以一般編碼規范都要求類的層次結構不要超過3層。組合是大型系統軟件實現即插即用時的首選方式。
最后還有一點,“優先使用對象組合,而不是繼承”是面向對象設計的第二原則。但並不是說什么都設計都用組合,只是優先考慮組合,更不是說繼承即使不好的設計,應該用組合,應為他們之間也有各自的優勢。