依賴倒置原則:一種特定的解耦形式,使得高層次模塊不依賴低層次模塊的實現細節的目的,依賴
被顛倒了。可以這麼理解,實現的細節依賴於抽象。那麼抽象又是什麼呢?可以理解爲一種約定好
的規則,在Java語言中,抽象具體指的是接口或抽象類,兩者都不能直接被實例化。細節就是實現
類,即實現接口或繼承抽象類而產生的類就是細節。其具體的表現爲,模塊間的依賴不通過細節類
發生,而是通過抽象(接口或者抽象類)發生。
以上都可以理解爲面向接口編程或者面向抽象(這裏指接口或者抽象類)編程。如果類與類直接直
接依賴於細節,他們之間就有直接的耦合,這樣當需求變化時,往往需要同時修改依賴和被依賴者
的代碼,這是我們應當避免出現的,原因就是很容易引入新bug,也限制了系統的擴展性能。
還是用第一、二篇中的例子來說明。
public class ImageLoader {
//內存緩存(直接依賴於細節),MemoryCache類參考上一篇文章
private MemoryCache mImageCache = new MemoryCache();
//線程池,線程數量爲CUP的數量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().
availableProcessors());
//UI Handler
Handler mUiHandler = new Handler(Looper.getMainLooper());
/**
* 提供外部調用顯示圖片的函數
*
* @param url 圖片的路徑
* @param imageview 顯示圖片的view
*/
public void displayImage(final String url, final ImageView imageview) {
Bitmap bitmap = mImageCache.get(url);
if (bitmap != null) {
imageview.setImageBitmap(bitmap);
return;
}
imageview.setTag(url);
mExecutorService.submit(new Runnable() {
@Override
public void run() {
Bitmap bitmap = downloadImage(url);
if (bitmap == null) return;
if (imageview.getTag().equals(url)) {
updateImageview(imageview, bitmap);
}
mImageCache.put(url, bitmap);
}
});
}
//部分代碼省略,具體參考《六大設計原則之開閉原則》
}
現在用戶覺得內存緩存已經不能滿足要求,還需要緩存到sd卡中。如果不用設計模式去實現,我們通
常的做法是:
1.實現一個可以用於sd卡緩存的類DiskCache
2.在ImageLoader中將ImageCache改爲DiskCache
但是如果用戶的需求再次改變呢?需要雙緩存,我們又得改原來的代碼,這就相當於ImageLoader
的實現依賴具體細節類ImageCache類或者DiskCache類,或者其他的,這不違反了上一篇的開閉
原則了嗎?也違反了本節中的依賴倒置原則。
那麼我們可以考慮下把這些具體細節共同的特性抽象出來,我們就可以依賴於這個規則去實現我們
的細節,其實就是依賴於抽象而不是細節。針對於圖片緩存,主要是圖片緩存的方式(內存、sd卡
或者雙緩存),從緩存的地方取出圖片這兩個功能。暫且不考慮各緩存方式是怎麼實現的,我們先
抽象出這兩個共性。所以我們建一個圖片緩存的接口:
public interface ImageCache {
void put(String url, Bitmap bitmap);
Bitmap get(String url);
}
在接口中有兩個方法,其中一個用於存,一個用於取。
這樣ImageLoader類就可以通過注入的方式依賴於這個接口,如下所示
public class ImageLoader {
//內存緩存,依賴於抽象,並且有一個默認的緩存策略MemoryCache,MemoryCache類參考上一篇文章
private ImageCache mImageCache = new MemoryCache();
//線程池,線程數量爲CUP的數量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().
availableProcessors());
//UI Handler
Handler mUiHandler = new Handler(Looper.getMainLooper());
/**
* 提供外部調用顯示圖片的函數
*
* @param url 圖片的路徑
* @param imageview 顯示圖片的view
*/
public void displayImage(final String url, final ImageView imageview) {
Bitmap bitmap = mImageCache.get(url);
if (bitmap != null) {
imageview.setImageBitmap(bitmap);
return;
}
imageview.setTag(url);
mExecutorService.submit(new Runnable() {
@Override
public void run() {
Bitmap bitmap = downloadImage(url);
if (bitmap == null) return;
if (imageview.getTag().equals(url)) {
updateImageview(imageview, bitmap);
}
mImageCache.put(url, bitmap);
}
});
}
/**
*設置注入的緩存策略,依賴於對象
*/
public void setImageCache(ImageCache cache){
mImageCache = cache;
}
//部分代碼省略,具體參考《六大設計原則之開閉原則》
}
最後,我們想用哪種緩存策略,只要實現接口ImageCache接口即可,然後通過ImageLoader
對象中的setImageCache()注入即可。這樣我們就不需要去更改ImageLoader中的代碼了。