如果說開閉原則是面向?qū)ο笤O(shè)計(jì)的目標(biāo)的話,那么依賴倒轉(zhuǎn)原則就是面向?qū)ο笤O(shè)計(jì)的主要實(shí)現(xiàn)機(jī)制之一湿蛔,它是系統(tǒng)抽象化的具體實(shí)現(xiàn)丁屎。依賴倒轉(zhuǎn)原則是·Robert C. Martin·在1996年為“C++Reporter”所寫的專欄Engineering Notebook的第三篇瘫筐,后來加入到他在2002年出版的經(jīng)典著作“Agile Software Development, Principles, Patterns, and Practices”一書中。依賴倒轉(zhuǎn)原則定義如下:
依賴倒轉(zhuǎn)原則(Dependency Inversion Principle, DIP):抽象不應(yīng)該依賴于細(xì)節(jié)喂链,細(xì)節(jié)應(yīng)當(dāng)依賴于抽象返十。換言之,要針對接口編程衩藤,而不是針對實(shí)現(xiàn)編程吧慢。
依賴倒轉(zhuǎn)原則要求我們在程序代碼中傳遞參數(shù)時(shí)或在關(guān)聯(lián)關(guān)系中,盡量引用層次高的抽象層類赏表,即使用接口和抽象類進(jìn)行變量類型聲明检诗、參數(shù)類型聲明、方法返回類型聲明瓢剿,以及數(shù)據(jù)類型的轉(zhuǎn)換等逢慌,而不要用具體類來做這些事情。
為了確保該原則的應(yīng)用间狂,一個(gè)具體類應(yīng)當(dāng)只實(shí)現(xiàn)接口或抽象類中聲明過的方法攻泼,而不要給出多余的方法,否則將無法調(diào)用到在子類中增加的新方法。
在引入抽象層后忙菠,系統(tǒng)將具有很好的靈活性何鸡,在程序中盡量使用抽象層進(jìn)行編程,而將具體類寫在配置文件中牛欢,這樣一來骡男,如果系統(tǒng)行為發(fā)生變化,只需要對抽象層進(jìn)行擴(kuò)展傍睹,并修改配置文件隔盛,而無須修改原有系統(tǒng)的源代碼,在不修改的情況下來擴(kuò)展系統(tǒng)的功能拾稳,滿足開閉原則的要求吮炕。
在實(shí)現(xiàn)依賴倒轉(zhuǎn)原則時(shí),我們需要針對抽象層編程访得,而將具體類的對象通過依賴注入(DependencyInjection, DI)
的方式注入到其他對象中龙亲,依賴注入是指當(dāng)一個(gè)對象要與其他對象發(fā)生依賴關(guān)系時(shí),通過抽象來注入所依賴的對象
震鹉。
常用的注入方式有三種俱笛,分別是:構(gòu)造注入
,設(shè)值注入(Setter注入)
和接口注入
传趾。
構(gòu)造注入是指通過構(gòu)造函數(shù)來傳入具體類的對象迎膜,設(shè)值注入是指通過Setter方法來傳入具體類的對象,而接口注入是指通過在接口中聲明的業(yè)務(wù)方法來傳入具體類的對象浆兰。這些方法在定義時(shí)使用的是抽象類型磕仅,在運(yùn)行時(shí)再傳入具體類型的對象,由子類對象來覆蓋父類對象簸呈。
下面通過一個(gè)簡單實(shí)例來加深對依賴倒轉(zhuǎn)原則的理解:
Sunny軟件公司開發(fā)人員在開發(fā)某CRM系統(tǒng)時(shí)發(fā)現(xiàn):該系統(tǒng)經(jīng)常需要將存儲(chǔ)在TXT或Excel文件中的客戶信息轉(zhuǎn)存到數(shù)據(jù)庫中榕订,因此需要進(jìn)行數(shù)據(jù)格式轉(zhuǎn)換。在客戶數(shù)據(jù)操作類中將調(diào)用數(shù)據(jù)格式轉(zhuǎn)換類的方法實(shí)現(xiàn)格式轉(zhuǎn)換和數(shù)據(jù)庫插入操作蜕便,初始設(shè)計(jì)方案結(jié)構(gòu)如圖1所示:
在編碼實(shí)現(xiàn)圖1所示結(jié)構(gòu)時(shí)劫恒,Sunny軟件公司開發(fā)人員發(fā)現(xiàn)該設(shè)計(jì)方案存在一個(gè)非常嚴(yán)重的問題,由于每次轉(zhuǎn)換數(shù)據(jù)時(shí)數(shù)據(jù)來源不一定相同轿腺,因此需要更換數(shù)據(jù)轉(zhuǎn)換類两嘴,如有時(shí)候需要將TXTDataConvertor改為ExcelDataConvertor,此時(shí)族壳,需要修改CustomerDAO的源代碼憔辫,而且在引入并使用新的數(shù)據(jù)轉(zhuǎn)換類時(shí)也不得不修改CustomerDAO的源代碼,系統(tǒng)擴(kuò)展性較差仿荆,違反了開閉原則贰您,現(xiàn)需要對該方案進(jìn)行重構(gòu)坏平。
在本實(shí)例中,由于CustomerDAO針對具體數(shù)據(jù)轉(zhuǎn)換類編程锦亦,因此在增加新的數(shù)據(jù)轉(zhuǎn)換類或者更換數(shù)據(jù)轉(zhuǎn)換類時(shí)都不得不修改CustomerDAO的源代碼舶替。我們可以通過引入抽象數(shù)據(jù)轉(zhuǎn)換類解決該問題,在引入抽象數(shù)據(jù)轉(zhuǎn)換類DataConvertor之后孽亲,CustomerDAO針對抽象類DataConvertor編程坎穿,而將具體數(shù)據(jù)轉(zhuǎn)換類名存儲(chǔ)在配置文件中,符合依賴倒轉(zhuǎn)原則返劲。根據(jù)里氏代換原則,程序運(yùn)行時(shí)栖茉,具體數(shù)據(jù)轉(zhuǎn)換類對象將替換DataConvertor類型的對象篮绿,程序不會(huì)出現(xiàn)任何問題。更換具體數(shù)據(jù)轉(zhuǎn)換類時(shí)無須修改源代碼吕漂,只需要修改配置文件亲配;如果需要增加新的具體數(shù)據(jù)轉(zhuǎn)換類,只要將新增數(shù)據(jù)轉(zhuǎn)換類作為DataConvertor的子類并修改配置文件即可惶凝,原有代碼無須做任何修改吼虎,滿足開閉原則。重構(gòu)后的結(jié)構(gòu)如圖2所示:
在上述重構(gòu)過程中苍鲜,我們使用了開閉原則思灰、里氏代換原則和依賴倒轉(zhuǎn)原則,在大多數(shù)情況下混滔,這三個(gè)設(shè)計(jì)原則會(huì)同時(shí)出現(xiàn)洒疚,開閉原則是目標(biāo),里氏代換原則是基礎(chǔ)坯屿,依賴倒轉(zhuǎn)原則是手段油湖,它們相輔相成,相互補(bǔ)充领跛,目標(biāo)一致乏德,只是分析問題時(shí)所站角度不同而已。
文章轉(zhuǎn)載自 —— 面向?qū)ο笤O(shè)計(jì)原則之依賴倒轉(zhuǎn)原則