前兩節(jié)我們?cè)敿?xì)介紹了面向?qū)ο笤O(shè)計(jì)原則中的開閉原則和里氏替換原則,在本節(jié)中我們來介紹依賴倒置原則。
依賴倒置原則(Dependence Inversion Principle,DIP)是 Object Mentor 公司總裁羅伯特·馬丁(Robert C.Martin)于 1996 年在 C++ Report 上發(fā)表的文章。
依賴倒置原則的原始定義為:高層模塊不應(yīng)該依賴低層模塊,兩者都應(yīng)該依賴其抽象;抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象(High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions)。其核心思想是:要面向接口編程,不要面向?qū)崿F(xiàn)編程。
依賴倒置原則是實(shí)現(xiàn)開閉原則的重要途徑之一,它降低了客戶與實(shí)現(xiàn)模塊之間的耦合。
由于在軟件設(shè)計(jì)中,細(xì)節(jié)具有多變性,而抽象層則相對(duì)穩(wěn)定,因此以抽象為基礎(chǔ)搭建起來的架構(gòu)要比以細(xì)節(jié)為基礎(chǔ)搭建起來的架構(gòu)要穩(wěn)定得多。這里的抽象指的是接口或者抽象類,而細(xì)節(jié)是指具體的實(shí)現(xiàn)類。
使用接口或者抽象類的目的是制定好規(guī)范和契約,而不去涉及任何具體的操作,把展現(xiàn)細(xì)節(jié)的任務(wù)交給它們的實(shí)現(xiàn)類去完成。
依賴倒置原則的主要作用如下。
• 依賴倒置原則可以降低類間的耦合性。
• 依賴倒置原則可以提高系統(tǒng)的穩(wěn)定性。
• 依賴倒置原則可以減少并行開發(fā)引起的風(fēng)險(xiǎn)。
• 依賴倒置原則可以提高代碼的可讀性和可維護(hù)性。
依賴倒置原則的目的是通過要面向接口的編程來降低類間的耦合性,所以我們?cè)趯?shí)際編程中只要遵循以下4點(diǎn),就能在項(xiàng)目中滿足這個(gè)規(guī)則。
1、每個(gè)類盡量提供接口或抽象類,或者兩者都具備。
2、變量的聲明類型盡量是接口或者是抽象類。
3、任何類都不應(yīng)該從具體類派生。
4、使用繼承時(shí)盡量遵循里氏替換原則。
下面以“顧客購物程序”為例來說明依賴倒置原則的應(yīng)用。
【例1】依賴倒置原則在“顧客購物程序”中的應(yīng)用。
分析:本程序反映了 “顧客類”與“商店類”的關(guān)系。商店類中有 sell() 方法,顧客類通過該方法購物以下代碼定義了顧客類通過韶關(guān)網(wǎng)店 ShaoguanShop 購物:
class Customer
{
public void shopping(ShaoguanShop shop)
{
//購物
System.out.println(shop.sell());
}
}
但是,這種設(shè)計(jì)存在缺點(diǎn),如果該顧客想從另外一家商店(如婺源網(wǎng)店 WuyuanShop)購物,就要將該顧客的代碼修改如下:
class Customer
{
public void shopping(WuyuanShop shop)
{
//購物
System.out.println(shop.sell());
}
}
顧客每更換一家商店,都要修改一次代碼,這明顯違背了開閉原則。存在以上缺點(diǎn)的原因是:顧客類設(shè)計(jì)時(shí)同具體的商店類綁定了,這違背了依賴倒置原則。解決方法是:定義“婺源網(wǎng)店”和“韶關(guān)網(wǎng)店”的共同接口 Shop,顧客類面向該接口編程,其代碼修改如下:
class Customer
{
public void shopping(Shop shop)
{
//購物
System.out.println(shop.sell());
}
}
這樣,不管顧客類 Customer 訪問什么商店,或者增加新的商店,都不需要修改原有代碼了,其類圖如圖 1 所示。
圖1 顧客購物程序的類圖
程序代碼如下:
package principle;
public class DIPtest
{
public static void main(String[] args)
{
Customer wang=new Customer();
System.out.println("顧客購買以下商品:");
wang.shopping(new ShaoguanShop());
wang.shopping(new WuyuanShop());
}
}
//商店
interface Shop
{
public String sell(); //賣
}
//韶關(guān)網(wǎng)店
class ShaoguanShop implements Shop
{
public String sell()
{
return "韶關(guān)土特產(chǎn):香菇、木耳……";
}
}
//婺源網(wǎng)店
class WuyuanShop implements Shop
{
public String sell()
{
return "婺源土特產(chǎn):綠茶、酒糟魚……";
}
}
//顧客
class Customer
{
public void shopping(Shop shop)
{
//購物
System.out.println(shop.sell());
}
}
程序的運(yùn)行結(jié)果如下:
顧客購買以下商品:
韶關(guān)土特產(chǎn):香菇、木耳……
婺源土特產(chǎn):綠茶、酒糟魚……