六大設計原則之依賴倒置原則

依賴倒置原則:一種特定的解耦形式,使得高層次模塊不依賴低層次模塊的實現細節的目的,依賴
被顛倒了。可以這麼理解,實現的細節依賴於抽象。那麼抽象又是什麼呢?可以理解爲一種約定好
的規則,在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中的代碼了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章