依賴倒置原則英文全稱是Dependence Inversion Principle莹汤,縮寫是DIP崭放。依賴倒置原則指代了一種特定解耦形式锌介,使得高層次的模塊不依賴于低層次的模塊的實現(xiàn)細(xì)節(jié)的目的巾表,依賴模塊被顛倒了猛计。這到底是什么意思呢?
依賴倒置原則則有以下關(guān)鍵點:
- 高層模塊不應(yīng)該依賴底層模塊绵估,兩者都應(yīng)該依賴其抽象炎疆;
- 抽象不應(yīng)該依賴細(xì)節(jié)。
- 細(xì)節(jié)應(yīng)該依賴抽象国裳。
在Java語言中形入,抽象就是指接口或抽象類,兩者都是不能直接被實例化的缝左;細(xì)節(jié)就是實現(xiàn)類亿遂,實現(xiàn)接口或者繼承抽象類而產(chǎn)生的類就是細(xì)節(jié)螟蒸,其特點就是,可以直接被實例化崩掘,也就是可以加上關(guān)鍵字new產(chǎn)生的對象七嫌。高層模塊就是調(diào)用端,底層模塊就是具體實現(xiàn)類苞慢。
依賴倒置原則在Java語言中的表現(xiàn)就是:模塊間的依賴通過抽象發(fā)生诵原,實現(xiàn)類之間不發(fā)生直接的依賴關(guān)系,其依賴關(guān)系是通過接口或者抽象類產(chǎn)生的挽放。概況為一句話就是:面向接口編程绍赛,或者說是面向抽象編程,這里的抽象只得就是接口或抽象類辑畦。面向接口是面向?qū)ο蟮木柚弧?/p>
如果類與類直接依賴于細(xì)節(jié)吗蚌,那么他們之間就有直接的耦合,當(dāng)具體實現(xiàn)需要變化時纯出,意味著需要同時修改依賴者的代碼蚯妇,這限制了系統(tǒng)的可擴(kuò)展性。
在前面的面向?qū)ο缶幊讨_閉原則這一節(jié)中暂筝,ImageLoader直接依賴于MemoryCache箩言,這個MemoryCache事一個具體實現(xiàn),而不是一個抽象類或者接口焕襟。這就導(dǎo)致了ImageLoader直接依賴了具體的細(xì)節(jié)陨收,當(dāng)MemoryCache不能滿足ImageLoader而需要被其他緩存實現(xiàn)替換時,此時就必須修改ImageLoader的代碼:
public class ImageLoader {
/**
* 圖片緩存
*/
MemoryCache mMemoryCache = new MemoryCache();
/**
* 顯示圖片
* @param url
* @param imageView
*/
public void displayImage(final String url, final ImageView imageView) {
//判斷使用哪種緩存
Bitmap bitmap = mMemoryCache.get(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return ;
}
dowloadImage(url,imageView);
}
隨著產(chǎn)品的升級鸵赖,用戶發(fā)現(xiàn)MemoryCache以及不能滿足需求务漩,用戶需要小民的ImageLoader可以將圖片同時緩存到內(nèi)存和SD卡中,或者可以讓用戶自定義實現(xiàn)緩存它褪。此時饵骨,我們的 MemoryCache這個類名不僅不能夠表達(dá)內(nèi)存緩存和SD卡緩存的意義,也不能滿足功能列赎。另外宏悦,用戶需要自定義緩存實現(xiàn)時還必須繼承自MemoryCache镐确,而用戶的緩存實現(xiàn)可不一定與內(nèi)存緩存有關(guān)包吝,這在命名上的限制也讓用戶體驗不好。重構(gòu)的時候到了源葫!第一種方案是將MemoryCache修改為DoubleCache诗越,然后在DoubleCache中實現(xiàn)具體的緩存功能。我們需要將ImageLoader修改如下:
public class ImageLoader {
/**
* 圖片緩存
*/
DoubleCache mDoubleCache = new DoubleCache();
/**
* 顯示圖片
* @param url
* @param imageView
*/
public void displayImage(final String url, final ImageView imageView) {
//判斷使用哪種緩存
Bitmap bitmap = mDoubleCache.get(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return ;
}
dowloadImage(url,imageView);
}
在程序中我們將MemoryCache修改成DoubleCache息堂,然后修改了ImageLoader中緩存類的具體實現(xiàn)嚷狞,輕輕松松就滿足了用戶需求块促。這不還是依賴具體的實現(xiàn)類嗎?當(dāng)用戶的需求發(fā)生變化時床未,我們又要通過修改緩存實現(xiàn)類和ImageLoader代碼來實現(xiàn)竭翠?
針對這些問題,給出的解決方案就要能夠讓緩存系統(tǒng)更加靈活薇搁。一句話概括起來就是:依賴抽象斋扰,而不是依賴具體實現(xiàn)。針對圖片緩存啃洋,建立ImageCache抽象(開閉原則小節(jié)所述)传货,該抽象增加了get和put方法用以實現(xiàn)圖片的存取。每種緩存實現(xiàn)都必須實現(xiàn)這個接口宏娄,并且實現(xiàn)自己的存取方法问裕。當(dāng)用戶需要使用不同的緩存實現(xiàn)時,直接通過依賴注入即可孵坚,保證了系統(tǒng)的靈活性粮宛。我們來回顧一下相關(guān)代碼:
ImageCache緩存接口類:
public interface ImageCache {
/**
* 獲取圖片
* @param url
* @return
*/
public Bitmap get(String url);
/**
* 緩存圖片
* @param url
* @param bitmap
*/
public void put(String url, Bitmap bitmap);
}
ImageLoader類:
public class ImageLoader {
/**
* 圖片緩存
*/
ImageCache mImageCache = new MemoryCache();
/**
* 注入緩存實現(xiàn)
* @param cache
*/
public void setIamgeCache(ImageCache cache) {
mImageCache = cache;
}
public void displayImage(final String url, final ImageView imageView) {
//判斷使用哪種緩存
Bitmap bitmap = mImageCache.get(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return ;
}
dowloadImage(url,imageView);
在這里,我們建立了ImageCache抽象類卖宠,并且讓ImageLoader依賴于抽象而不是具體細(xì)節(jié)窟勃。當(dāng)需求發(fā)生變化時,只要實現(xiàn)ImageCache類或者繼承其他已有的ImageCache子類完成相應(yīng)的緩存功能逗堵,然后將具體的實現(xiàn)類注入到ImageLoader即可實現(xiàn)緩存功能的替換秉氧,這就保證的緩存系統(tǒng)的高可擴(kuò)展性,有了擁抱變化的能力蜒秤,這就是依賴倒置原則汁咏。