合成聚合復(fù)用原則_動力節(jié)點Java學(xué)院整理
合成聚合復(fù)用原則
合成復(fù)用原則又稱為組合/聚合復(fù)用原則(Composition/Aggregate Reuse Principle, CARP),其定義如下:
合成復(fù)用原則(Composite Reuse Principle, CRP):盡量使用對象組合,而不是繼承來達(dá)到復(fù)用的目的。
合成復(fù)用原則就是在一個新的對象里通過關(guān)聯(lián)關(guān)系(包括組合關(guān)系和聚合關(guān)系)來使用一些已有的對象,使之成為新對象的一部分;新對象通過委派調(diào)用已有對象的方法達(dá)到復(fù)用功能的目的。簡言之:復(fù)用時要盡量使用組合/聚合關(guān)系(關(guān)聯(lián)關(guān)系),少用繼承。
在面向?qū)ο笤O(shè)計中,可以通過兩種方法在不同的環(huán)境中復(fù)用已有的設(shè)計和實現(xiàn),即通過組合/聚合關(guān)系或通過繼承,但首先應(yīng)該考慮使用組合/聚合,組合/聚合可以使系統(tǒng)更加靈活,降低類與類之間的耦合度,一個類的變化對其他類造成的影響相對較少;其次才考慮繼承,在使用繼承時,需要嚴(yán)格遵循里氏代換原則,有效使用繼承會有助于對問題的理解,降低復(fù)雜度,而濫用繼承反而會增加系統(tǒng)構(gòu)建和維護(hù)的難度以及系統(tǒng)的復(fù)雜度,因此需要慎重使用繼承復(fù)用。
通過繼承來進(jìn)行復(fù)用的主要問題在于繼承復(fù)用會破壞系統(tǒng)的封裝性,因為繼承會將基類的實現(xiàn)細(xì)節(jié)暴露給子類,由于基類的內(nèi)部細(xì)節(jié)通常對子類來說是可見的,所以這種復(fù)用又稱“白箱”復(fù)用,如果基類發(fā)生改變,那么子類的實現(xiàn)也不得不發(fā)生改變;從基類繼承而來的實現(xiàn)是靜態(tài)的,不可能在運行時發(fā)生改變,沒有足夠的靈活性;而且繼承只能在有限的環(huán)境中使用(如類沒有聲明為不能被繼承)。
擴(kuò)展
對于繼承的深入理解,大家可以參考《軟件架構(gòu)設(shè)計》一書作者溫昱先生的文章——《見山只是山見水只是水——提升對繼承的認(rèn)識》。
由于組合或聚合關(guān)系可以將已有的對象(也可稱為成員對象)納入到新對象中,使之成為新對象的一部分,因此新對象可以調(diào)用已有對象的功能,這樣做可以使得成員對象的內(nèi)部實現(xiàn)細(xì)節(jié)對于新對象不可見,所以這種復(fù)用又稱為“黑箱”復(fù)用,相對繼承關(guān)系而言,其耦合度相對較低,成員對象的變化對新對象的影響不大,可以在新對象中根據(jù)實際需要有選擇性地調(diào)用成員對象的操作;合成復(fù)用可以在運行時動態(tài)進(jìn)行,新對象可以動態(tài)地引用與成員對象類型相同的其他對象。
一般而言,如果兩個類之間是“Has-A”的關(guān)系應(yīng)使用組合或聚合,如果是“Is-A”關(guān)系可使用繼承。"Is-A"是嚴(yán)格的分類學(xué)意義上的定義,意思是一個類是另一個類的"一種";而"Has-A"則不同,它表示某一個角色具有某一項責(zé)任。
下面通過一個簡單實例來加深對合成復(fù)用原則的理解:
Sunny軟件公司開發(fā)人員在初期的CRM系統(tǒng)設(shè)計中,考慮到客戶數(shù)量不多,系統(tǒng)采用MySQL作為數(shù)據(jù)庫,與數(shù)據(jù)庫操作有關(guān)的類如CustomerDAO類等都需要連接數(shù)據(jù)庫,連接數(shù)據(jù)庫的方法getConnection()封裝在DBUtil類中,由于需要重用DBUtil類的getConnection()方法,設(shè)計人員將CustomerDAO作為DBUtil類的子類,初始設(shè)計方案結(jié)構(gòu)如圖1所示:
圖1 初始設(shè)計方案結(jié)構(gòu)圖
隨著客戶數(shù)量的增加,系統(tǒng)決定升級為Oracle數(shù)據(jù)庫,因此需要增加一個新的OracleDBUtil類來連接Oracle數(shù)據(jù)庫,由于在初始設(shè)計方案中CustomerDAO和DBUtil之間是繼承關(guān)系,因此在更換數(shù)據(jù)庫連接方式時需要修改CustomerDAO類的源代碼,將CustomerDAO作為OracleDBUtil的子類,這將違反開閉原則?!井?dāng)然也可以修改DBUtil類的源代碼,同樣會違反開閉原則。】
現(xiàn)使用合成復(fù)用原則對其進(jìn)行重構(gòu)。
根據(jù)合成復(fù)用原則,我們在實現(xiàn)復(fù)用時應(yīng)該多用關(guān)聯(lián),少用繼承。因此在本實例中我們可以使用關(guān)聯(lián)復(fù)用來取代繼承復(fù)用,重構(gòu)后的結(jié)構(gòu)如圖2所示:
圖2 重構(gòu)后的結(jié)構(gòu)圖
在圖2中,CustomerDAO和DBUtil之間的關(guān)系由繼承關(guān)系變?yōu)殛P(guān)聯(lián)關(guān)系,采用依賴注入的方式將DBUtil對象注入到CustomerDAO中,可以使用構(gòu)造注入,也可以使用Setter注入。如果需要對DBUtil的功能進(jìn)行擴(kuò)展,可以通過其子類來實現(xiàn),如通過子類OracleDBUtil來連接Oracle數(shù)據(jù)庫。由于CustomerDAO針對DBUtil編程,根據(jù)里氏代換原則,DBUtil子類的對象可以覆蓋DBUtil對象,只需在CustomerDAO中注入子類對象即可使用子類所擴(kuò)展的方法。例如在CustomerDAO中注入OracleDBUtil對象,即可實現(xiàn)Oracle數(shù)據(jù)庫連接,原有代碼無須進(jìn)行修改,而且還可以很靈活地增加新的數(shù)據(jù)庫連接方式。
相關(guān)文章
Java隨機值設(shè)置(java.util.Random類或Math.random方法)
在編程中有時我們需要生成一些隨機的字符串作為授權(quán)碼、驗證碼等,以確保數(shù)據(jù)的安全性和唯一性,這篇文章主要給大家介紹了關(guān)于Java隨機值設(shè)置的相關(guān)資料,主要用的是java.util.Random類或Math.random()方法,需要的朋友可以參考下2024-08-08Java如何將任意類型的Object對象轉(zhuǎn)換為相應(yīng)的實體對象
這篇文章主要介紹了Java如何將任意類型的Object對象轉(zhuǎn)換為相應(yīng)的實體對象問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01springboot2.3 整合mybatis-plus 高級功能(圖文詳解)
這篇文章主要介紹了springboot2.3 整合mybatis-plus 高級功能,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08透過Spring源碼查看Bean的命名轉(zhuǎn)換規(guī)則圖文詳解
Java Bean是一種 Java 編程語言編寫的可重用軟件組件,包括符合一定規(guī)范的Java 類、屬性和方法,用于描述和處理應(yīng)用程序中的數(shù)據(jù)對象,下面這篇文章主要給大家介紹了關(guān)于透過Spring源碼查看Bean的命名轉(zhuǎn)換規(guī)則的相關(guān)資料,需要的朋友可以參考下2023-06-06java實現(xiàn)字符串匹配求兩個字符串的最大公共子串
這篇文章主要介紹了java實現(xiàn)求兩個字符串最大公共子串的方法,詳細(xì)的描述了兩個字符串的最大公共子串算法的實現(xiàn),需要的朋友可以參考下2016-10-10