深刻理解Java中的組合和繼承

Java是一個面向對象的語言。每個學習過Java的人都知道,封裝、繼承、多態是面向對象的三個特徵。每一個人在剛剛學習繼承的時候都會或多或少的有這樣一個印象:繼承能夠幫助我實現類的複用。因此,不少開發人員在須要複用一些代碼的時候會很天然的使用類的繼承的方式,由於書上就是這麼寫的(老師就是這麼教的)。可是,其實這樣作是不對的。長期大量的使用繼承會給代碼帶來很高的維護成本。編程

本文將介紹組合和繼承的概念及區別,並從多方面分析在寫代碼時如何進行選擇。安全

面向對象的複用技術

前面提到複用,這裏就簡單介紹一下面向對象的複用技術。網絡

複用性是面向對象技術帶來的很棒的潛在好處之一。若是運用的好的話能夠幫助咱們節省不少開發時間,提高開發效率。可是,若是被濫用那麼就可能產生不少難以維護的代碼。學習

做爲一門面向對象開發的語言,代碼複用是Java引人注意的功能之一。Java代碼的複用有繼承,組合以及代理三種具體的表現形式。本文將重點介紹繼承複用和組合複用。spa

繼承

繼承(Inheritance)是一種聯結類與類的層次模型。指的是一個類(稱爲子類、子接口)繼承另外的一個類(稱爲父類、父接口)的功能,並能夠增長它本身的新功能的能力,繼承是類與類或者接口與接口之間最多見的關係;繼承是一種is-a關係。(圖片來自網絡,侵刪。)代理

組合

組合(Composition)體現的是總體與部分、擁有的關係,即has-a的關係。code

組合與繼承的區別和聯繫

繼承結構中,父類的內部細節對於子類是可見的。因此咱們一般也能夠說經過繼承的代碼複用是一種白盒式代碼複用。(若是基類的實現發生改變,那麼派生類的實現也將隨之改變。這樣就致使了子類行爲的不可預知性;)對象

組合是經過對現有的對象進行拼裝(組合)產生新的、更復雜的功能。由於在對象之間,各自的內部細節是不可見的,因此咱們也說這種方式的代碼複用是黑盒式代碼複用。(由於組合中通常都定義一個類型,因此在編譯期根本不知道具體會調用哪一個實現類的方法)繼承

繼承,在寫代碼的時候就要指名具體繼承哪一個類,因此,在編譯期就肯定了關係。(從基類繼承來的實現是沒法在運行期動態改變的,所以下降了應用的靈活性。)接口

組合,在寫代碼的時候能夠採用面向接口編程。因此,類的組合關係通常在運行期肯定。

優缺點對比

組 合 關 系 繼 承 關 系
優勢:不破壞封裝,總體類與局部類之間鬆耦合,彼此相對獨立 缺點:破壞封裝,子類與父類之間緊密耦合,子類依賴於父類的實現,子類缺少獨立性
優勢:具備較好的可擴展性 缺點:支持擴展,可是每每以增長系統結構的複雜度爲代價
優勢:支持動態組合。在運行時,總體對象能夠選擇不一樣類型的局部對象 缺點:不支持動態繼承。在運行時,子類沒法選擇不一樣的父類
優勢:總體類能夠對局部類進行包裝,封裝局部類的接口,提供新的接口 缺點:子類不能改變父類的接口
缺點:總體類不能自動得到和局部類一樣的接口 優勢:子類能自動繼承父類的接口
缺點:建立總體類的對象時,須要建立全部局部類的對象 優勢:建立子類的對象時,無須建立父類的對象

如何選擇

相信不少人都知道面向對象中有一個比較重要的原則『多用組合、少用繼承』或者說『組合優於繼承』。從前面的介紹已經優缺點對比中也能夠看出,組合確實比繼承更加靈活,也更有助於代碼維護。

因此,

建議在一樣可行的狀況下,優先使用組合而不是繼承。

由於組合更安全,更簡單,更靈活,更高效。

注意,並非說繼承就一點用都沒有了,前面說的是【在一樣可行的狀況下】。有一些場景仍是須要使用繼承的,或者是更適合使用繼承。

繼承要慎用,其使用場合僅限於你確信使用該技術有效的狀況。一個判斷方法是,問一問本身是否須要重新類向基類進行向上轉型。若是是必須的,則繼承是必要的。反之則應該好好考慮是否須要繼承。《Java編程思想》

只有當子類真正是超類的子類型時,才適合用繼承。換句話說,對於兩個類A和B,只有當二者之間確實存在is-a關係的時候,類B才應該繼續類A。《Effective Java》

相關文章
相關標籤/搜索