面向對象設計原則之合成複用原則

合成複用原則又稱爲組合/聚合複用原則(Composition/Aggregate Reuse Principle, CARP),其定義以下:數據庫

合成複用原則(Composite Reuse Principle, CRP):儘可能使用對象組合,而不是繼承來達到複用的目的。編程

      合成複用原則就是在一個新的對象裏經過關聯關係(包括組合關係和聚合關係)來使用一些已有的對象,使之成爲新對象的一部分;新對象經過委派調用已有對象的方法達到複用功能的目的。簡言之:複用時要儘可能使用組合/聚合關係(關聯關係),少用繼承架構

      在面向對象設計中,能夠經過兩種方法在不一樣的環境中複用已有的設計和實現,即經過組合/聚合關係或經過繼承,但首先應該考慮使用組合/聚合,組合/聚合可使系統更加靈活,下降類與類之間的耦合度,一個類的變化對其餘類形成的影響相對較少;其次才考慮繼承,在使用繼承時,須要嚴格遵循里氏代換原則,有效使用繼承會有助於對問題的理解,下降複雜度,而濫用繼承反而會增長系統構建和維護的難度以及系統的複雜度,所以須要慎重使用繼承複用。spa

      經過繼承來進行復用的主要問題在於繼承複用會破壞系統的封裝性,由於繼承會將基類的實現細節暴露給子類,因爲基類的內部細節一般對子類來講是可見的,因此這種複用又稱「白箱」複用,若是基類發生改變,那麼子類的實現也不得不發生改變;從基類繼承而來的實現是靜態的,不可能在運行時發生改變,沒有足夠的靈活性;並且繼承只能在有限的環境中使用(如類沒有聲明爲不能被繼承)。架構設計

 

擴展設計

對於繼承的深刻理解,你們能夠參考《軟件架構設計》一書做者溫昱先生的文章——《見山只是山見水只是水——提高對繼承的認識》。對象

 

      因爲組合或聚合關係能夠將已有的對象(也可稱爲成員對象)歸入到新對象中,使之成爲新對象的一部分,所以新對象能夠調用已有對象的功能,這樣作可使得成員對象的內部實現細節對於新對象不可見,因此這種複用又稱爲「黑箱」複用,相對繼承關係而言,其耦合度相對較低,成員對象的變化對新對象的影響不大,能夠在新對象中根據實際須要有選擇性地調用成員對象的操做;合成複用能夠在運行時動態進行,新對象能夠動態地引用與成員對象類型相同的其餘對象。繼承

      通常而言,若是兩個類之間是「Has-A」的關係應使用組合或聚合,若是是「Is-A」關係可以使用繼承。"Is-A"是嚴格的分類學意義上的定義,意思是一個類是另外一個類的"一種";而"Has-A"則不一樣,它表示某一個角色具備某一項責任。ip

      下面經過一個簡單實例來加深對合成複用原則的理解:ci

      Sunny軟件公司開發人員在初期的CRM系統設計中,考慮到客戶數量很少,系統採用MySQL做爲數據庫,與數據庫操做有關的類如CustomerDAO類等都須要鏈接數據庫,鏈接數據庫的方法getConnection()封裝在DBUtil類中,因爲須要重用DBUtil類的getConnection()方法,設計人員將CustomerDAO做爲DBUtil類的子類,初始設計方案結構如圖1所示:

圖1  初始設計方案結構圖

      隨着客戶數量的增長,系統決定升級爲Oracle數據庫,所以須要增長一個新的OracleDBUtil類來鏈接Oracle數據庫,因爲在初始設計方案中CustomerDAO和DBUtil之間是繼承關係,所以在更換數據庫鏈接方式時須要修改CustomerDAO類的源代碼,將CustomerDAO做爲OracleDBUtil的子類,這將違反開閉原則。【固然也能夠修改DBUtil類的源代碼,一樣會違反開閉原則。】

      現使用合成複用原則對其進行重構。

      根據合成複用原則,咱們在實現複用時應該多用關聯,少用繼承。所以在本實例中咱們可使用關聯複用來取代繼承複用,重構後的結構如圖2所示:

圖2  重構後的結構圖

      在圖2中,CustomerDAO和DBUtil之間的關係由繼承關係變爲關聯關係,採用依賴注入的方式將DBUtil對象注入到CustomerDAO中,可使用構造注入,也可使用Setter注入。若是須要對DBUtil的功能進行擴展,能夠經過其子類來實現,如經過子類OracleDBUtil來鏈接Oracle數據庫。因爲CustomerDAO針對DBUtil編程,根據里氏代換原則,DBUtil子類的對象能夠覆蓋DBUtil對象,只需在CustomerDAO中注入子類對象便可使用子類所擴展的方法。例如在CustomerDAO中注入OracleDBUtil對象,便可實現Oracle數據庫鏈接,原有代碼無須進行修改,並且還能夠很靈活地增長新的數據庫鏈接方式。

相關文章
相關標籤/搜索