面向?qū)ο蟮膸状笤瓌t1

一、開閉原則

是面向?qū)ο蟮目蓮?fù)用設(shè)計的第一塊基石,它是最重要的面向?qū)ο笤O(shè)計原則。

定義:開閉原則(Open-Closed Principle, OCP):一個軟件實體應(yīng)當(dāng)對擴(kuò)展開放,對修改關(guān)閉。即軟件實體應(yīng)盡量在不修改原有代碼的情況下進(jìn)行擴(kuò)展。

抽象化是開閉原則的關(guān)鍵,可以用面向?qū)ο缶幊陶Z言中提供的接口、抽象類等機(jī)制來實現(xiàn)

二、里氏代換原則

其嚴(yán)格表述如下:如果對每一個類型為S的對象o1,都有類型為T的對象o2,使得以T定義的所有程序P在所有的對象o1代換o2時,程序P的行為沒有變化,那么類型S是類型T的子類型。這個定義比較拗口且難以理解,因此我們一般使用它的另一個通俗版定義:

里氏代換原則(Liskov Substitution Principle, LSP):所有引用基類(父類)的地方必須能透明地使用其子類的對象

里氏代換原則告訴我們,在軟件中將一個基類對象替換成它的子類對象,程序?qū)⒉粫a(chǎn)生任何錯誤和異常,反過來則不成立,如果一個軟件實體使用的是一個子類對象的話,那么它不一定能夠使用基類對象。例如:我喜歡動物,那我一定喜歡狗,因為狗是動物的子類;但是我喜歡狗,不能據(jù)此斷定我喜歡動物,因為我并不喜歡老鼠,雖然它也是動物。
里氏代換原則是實現(xiàn)開閉原則的重要方式之一,由于使用基類對象的地方都可以使用子類對象,因此在程序中盡量使用基類類型來對對象進(jìn)行定義,而在運(yùn)行時再確定其子類類型,用子類對象來替換父類對象。
在使用里氏代換原則時需要注意如下幾個問題:

  (1)子類的所有方法必須在父類中聲明,或子類必須實現(xiàn)父類中聲明的所有方法。根據(jù)里氏代換原則,為了保證系統(tǒng)的擴(kuò)展性,在程序中通常使用父類來進(jìn)行定義,如果一個方法只存在子類中,在父類中不提供相應(yīng)的聲明,則無法在以父類定義的對象中使用該方法。

  (2)  我們在運(yùn)用里氏代換原則時,盡量把父類設(shè)計為抽象類或者接口,讓子類繼承父類或?qū)崿F(xiàn)父接口,并實現(xiàn)在父類中聲明的方法,運(yùn)行時,子類實例替換父類實例,我們可以很方便地擴(kuò)展系統(tǒng)的功能,同時無須修改原有子類的代碼,增加新的功能可以通過增加一個新的子類來實現(xiàn)。里氏代換原則是開閉原則的具體實現(xiàn)手段之一。

  (3) Java語言中,在編譯階段,Java編譯器會檢查一個程序是否符合里氏代換原則,這是一個與實現(xiàn)無關(guān)的、純語法意義上的檢查,但Java編譯器的檢查是有局限的。

總結(jié)一句話:在傳遞參數(shù)時使用基類對象,除此以外,在定義成員變量、定義局部變量、確定方法返回類型時都可使用里氏代換原則。針對基類編程,在程序運(yùn)行時再確定具體子類。

三、依賴倒轉(zhuǎn)原則

開閉原則是目標(biāo),里氏代換原則是基礎(chǔ),依賴倒轉(zhuǎn)原則是手段

依賴倒轉(zhuǎn)原則(Dependency Inversion Principle, DIP):抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)當(dāng)依賴于抽象(倒轉(zhuǎn)了)。換言之,要針對接口(抽象類)編程,而不是針對實現(xiàn)編程。

自己理解:先框架再細(xì)節(jié),之前是先考慮細(xì)節(jié)后框架,所以倒轉(zhuǎn)了。應(yīng)該針對抽象類編程意思是,參數(shù)等應(yīng)該定義為抽象類類型,不要定義為具體的實現(xiàn)類,具體實現(xiàn)類是運(yùn)行時傳遞的

具體實現(xiàn)方式:

依賴倒轉(zhuǎn)原則要求我們在程序代碼中傳遞參數(shù)時或在關(guān)聯(lián)關(guān)系中,盡量引用層次高的抽象層類,即使用接口和抽象類進(jìn)行變量類型聲明、參數(shù)類型聲明、方法返回類型聲明,以及數(shù)據(jù)類型的轉(zhuǎn)換等,而不要用具體類來做這些事情。為了確保該原則的應(yīng)用,一個具體類應(yīng)當(dāng)只實現(xiàn)接口或抽象類中聲明過的方法,而不要給出多余的方法,否則將無法調(diào)用到在子類中增加的新方法。

好處:

在引入抽象層后,系統(tǒng)將具有很好的靈活性,在程序中盡量使用抽象層進(jìn)行編程,而將具體類寫在配置文件中,這樣一來,如果系統(tǒng)行為發(fā)生變化,只需要對抽象層進(jìn)行擴(kuò)展,并修改配置文件,而無須修改原有系統(tǒng)的源代碼,在不修改的情況下來擴(kuò)展系統(tǒng)的功能,滿足開閉原則的要求。

具體應(yīng)用場景,依賴注入

在實現(xiàn)依賴倒轉(zhuǎn)原則時,我們需要針對抽象層編程,而將具體類的對象通過依賴注入(DependencyInjection, DI)的方式注入到其他對象中,依賴注入是指當(dāng)一個對象要與其他對象發(fā)生依賴關(guān)系時,通過抽象來注入所依賴的對象。常用的注入方式有三種,分別是:構(gòu)造注入,設(shè)值注入(Setter注入)和接口注入。構(gòu)造注入是指通過構(gòu)造函數(shù)來傳入具體類的對象,設(shè)值注入是指通過Setter方法來傳入具體類的對象,而接口注入是指通過在接口中聲明的業(yè)務(wù)方法來傳入具體類的對象。這些方法在定義時使用的是抽象類型,在運(yùn)行時再傳入具體類型的對象,由子類對象來覆蓋父類對象。

例子:

Sunny軟件公司開發(fā)人員在開發(fā)某CRM系統(tǒng)時發(fā)現(xiàn):該系統(tǒng)經(jīng)常需要將存儲在TXT或Excel文件中的客戶信息轉(zhuǎn)存到數(shù)據(jù)庫中,因此需要進(jìn)行數(shù)據(jù)格式轉(zhuǎn)換。在客戶數(shù)據(jù)操作類中將調(diào)用數(shù)據(jù)格式轉(zhuǎn)換類的方法實現(xiàn)格式轉(zhuǎn)換和數(shù)據(jù)庫插入操作,初始設(shè)計方案結(jié)構(gòu)如圖1所示:

圖1 初始設(shè)計方案結(jié)構(gòu)圖

在編碼實現(xiàn)圖1所示結(jié)構(gòu)時,Sunny軟件公司開發(fā)人員發(fā)現(xiàn)該設(shè)計方案存在一個非常嚴(yán)重的問題,由于每次轉(zhuǎn)換數(shù)據(jù)時數(shù)據(jù)來源不一定相同,因此需要更換數(shù)據(jù)轉(zhuǎn)換類,如有時候需要將TXTDataConvertor改為ExcelDataConvertor,此時,需要修改CustomerDAO的源代碼,而且在引入并使用新的數(shù)據(jù)轉(zhuǎn)換類時也不得不修改CustomerDAO的源代碼,系統(tǒng)擴(kuò)展性較差,違反了開閉原則,現(xiàn)需要對該方案進(jìn)行重構(gòu)。

在本實例中,由于CustomerDAO針對具體數(shù)據(jù)轉(zhuǎn)換類編程,因此在增加新的數(shù)據(jù)轉(zhuǎn)換類或者更換數(shù)據(jù)轉(zhuǎn)換類時都不得不修改CustomerDAO的源代碼。我們可以通過引入抽象數(shù)據(jù)轉(zhuǎn)換類解決該問題,在引入抽象數(shù)據(jù)轉(zhuǎn)換類DataConvertor之后,CustomerDAO針對抽象類DataConvertor編程,而將具體數(shù)據(jù)轉(zhuǎn)換類名存儲在配置文件中,符合依賴倒轉(zhuǎn)原則。根據(jù)里氏代換原則,程序運(yùn)行時,具體數(shù)據(jù)轉(zhuǎn)換類對象將替換DataConvertor類型的對象,程序不會出現(xiàn)任何問題。更換具體數(shù)據(jù)轉(zhuǎn)換類時無須修改源代碼,只需要修改配置文件;如果需要增加新的具體數(shù)據(jù)轉(zhuǎn)換類,只要將新增數(shù)據(jù)轉(zhuǎn)換類作為DataConvertor的子類并修改配置文件即可,原有代碼無須做任何修改,滿足開閉原則。重構(gòu)后的結(jié)構(gòu)如圖2所示:

圖2重構(gòu)后的結(jié)構(gòu)圖

在上述重構(gòu)過程中,我們使用了開閉原則、里氏代換原則和依賴倒轉(zhuǎn)原則,在大多數(shù)情況下,這三個設(shè)計原則會同時出現(xiàn),開閉原則是目標(biāo),里氏代換原則是基礎(chǔ),依賴倒轉(zhuǎn)原則是手段,它們相輔相成,相互補(bǔ)充,目標(biāo)一致,只是分析問題時所站角度不同而已。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容