提到面嚮對象語言中的繼承,可能你們都以爲很簡單,在日常工做中常用。這種編碼實現的特色不只是代碼重用的有力手段,更重要的是,它是java封裝特性的基石。可是在平常工做中有沒有想過是不是必須使用繼承,有沒有可替代的作法,或者繼承是否有濫用的狀況呢?java
其實談到繼承所實現的功能,組合複用也能打到相同的效果。好比如今有出庫和入庫兩種單據,都要實現根據單號查找對應的詳情的需求,可使用新建兩個繼承自訂單基類的出入庫子類的作法,來實現這個功能。這樣根據單號查找詳情的邏輯就被抽象到了上層的父類中。固然若是使用組合複用的作法,也能夠將根據單號查找詳情做爲一個單獨的類,由出入庫類調用這個類做爲組件,來實現這個功能。程序員
若是在平常工做中,遇到相似狀況你堅決果斷的選擇了繼承來打到你的目的,那就說明有問題了。實現一個目的不可能只有一個選擇。在本文中,我只闡述個人觀點,不要爲了複用代碼而去使用繼承。由於在java語境中,你extend了一個父類後,你的子類就複用了該基類的實現,而不能再採用另外的實現。而組合複用的方式則不會受到此類限制。安全
拋開上面提到的,其實繼承還有不少值得關注的點,通常咱們在看一些成熟框架的源碼的時候,會發現大部分繼承都是在同一個包中完成的。在包的內部使用繼承是很是安全的,由於這很大機率代表了該父類和繼承的子類是處在同一時空中,處在同一個程序員的控制之下。這種繼承很是安全。可是若是繼承操做跨了包了,也就證實這個繼承操做,打破了java的封裝性,由於子類的實現邏輯牽扯到了父類的邏輯。這種狀況下若是父類發生了演化那麼子類也要跟着一塊兒演化。就好像你在使用了某個框架的類,把它做爲父類,進行了繼承,可是在某個時間點你升級了該框架,那麼你實現的子類中的操做就可能存在着隱患,由於他所繼承的父類中可能發生了變更。app
這裏的變更多是修改也多是新增。修改操做比較好理解,那麼新增操做爲何會致使子類存在隱患呢?拿java中的集合來舉例說明,加入在父類和子類中存在着加入該集合須要知足某些條件的邏輯,子類覆蓋了全部添加的集合元素的方法,這種狀況下可以正常工做。可是如今父類增長了一個新的添加集合的方法,可是子類沒有去實現,這樣後果就是該集合中能夠添加不知足子類限制條件,可是知足父類限制條件的元素。這種狀況針對子類來講是危險的。框架
很容易想到一種方法來替代繼承,那就是在子類中增長一個私有域。這個私有域引用一個現有類的實現。這個實例中提供給不一樣的子類相同的方法。這種設計就叫作組合複用。用上面的出入庫訂單來舉例的話,出庫和入庫兩個類再也不繼承同一個父類,而是引入這樣一個類,它提供了公共的根據單號查詢出/入庫單據的方法。這種形式就至關於這兩個出入庫的子類包裝了具備公共方法的那個類,因此這兩個類也叫作包裝類(wrapper class)。這也正是Decorator模式,由於這兩個出入庫類修飾了共有方法類,爲他增長了新的特性。編碼
那麼何時才應該使用繼承呢?只有當子類真生是父類的子類型(subtype)時,才適合用繼承。換句話說,只有子類和父類存在「is a」關係的時候,子類才應該擴展父類。而不只僅是子類共有的方法聚合成一個父類。若是子類和父類不是「is a」的關係,這種狀況下這裏所謂的子類(其實這裏使用組合複用的模式就不存在子類與父類的關係了)應該包含父類的一個私有實例,而且這個私有實例只暴露一個較小的API。本質上這裏的子類不是父類的實現,只是父類的部分實現細節而已。spa
總結一點,繼承的功能很強大,可是要當心使用,由於它違背了封裝原則。只有當子類和超類之間確實存在子類型關係時,使用繼承纔是恰當的。即使如此,若是子類和超類處在不一樣的包中,而且超類並非爲了繼承而設計的,那麼繼承將會致使脆弱性。爲了不脆弱性,能夠用組合複用來代替繼承,尤爲是當存在在適當的接口能夠實現包裝類的時候。包裝類不只比子類更加健壯,並且功能也更增強大。設計