Android源碼設計模式解析與實戰(一)

結合Android源碼和圖片加載的例子,介紹設計模式的同時,在例子中實際運用,更易於讀者對設計模式的理解和使用。java

本篇博客結合書中圖片加載的例子和本身對知識點的理解,側重記錄下設計模式的使用,原理部分略過。編程

第一章 走向靈活軟件之路——面向對象的六大原則設計模式

1.1 優化代碼的第一步——單一職責原則緩存

    我的理解:「核心思想就是類的抽象和封裝,將相同功能的代碼封裝在一個類中,不一樣功能的相互調用經過引用類對象方式實現"ide

    下面引用書中的例子,對比下函數

    這個是書中的第一個版本,相信你們能發現不少問題,好比每次都建立線程池和緩存,這些後面會進行優化,這塊要說的是緩存的處理是一個獨立的功能,應該抽象封裝到一個類中。一樣,下載功能也是如此。優化

public class ImageLoader {

    private final int BYTE_UNIT = 1024;

    // 圖片緩存
    private LruCache<String, Bitmap> mImageCache;
    // 線程池,線程數量爲CPU的數量
    private ExecutorService mExecutorService;

    public ImageLoader() {
        initImageCache();
        mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    }

    private void initImageCache() {
        // 計算可以使用的最大內存
        final long maxMemory = Runtime.getRuntime().maxMemory() / BYTE_UNIT;
        final int memorySize = (int) maxMemory / 4;
        mImageCache = new LruCache<String, Bitmap>(memorySize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    // API19
                    return value.getAllocationByteCount() / BYTE_UNIT;
                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) {
                    // API12
                    return value.getByteCount() / 1024;
                }
                // Bitmap所佔用的內存空間數等於Bitmap的每一行所佔用的空間數乘以Bitmap的行數
                return value.getRowBytes() * value.getHeight() / BYTE_UNIT;
            }
        };
    }

    /**
     * 加載圖片
     * @param url
     * @param imageView
     */
    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)) {
                    imageView.setImageBitmap(bitmap);
                }
                mImageCache.put(url, bitmap);
            }
        });
    }
    
    private Bitmap downloadImage(String url) {
        Bitmap bitmap = null;
        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) new URL(url).openConnection();
            bitmap = BitmapFactory.decodeStream(connection.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
        return bitmap;
    }
}

    書中在本節只對緩存功能作了封裝,咱們來看下封裝後的代碼。ui

public class ImageCache {
    private final int BYTE_UNIT = 1024;

    // 圖片緩存
    private LruCache<String, Bitmap> mImageCache;

    public ImageCache() {
        initImageCache();
    }

    private void initImageCache() {
        // 計算可以使用的最大內存
        final long maxMemory = Runtime.getRuntime().maxMemory() / BYTE_UNIT;
        final int memorySize = (int) maxMemory / 4;
        mImageCache = new LruCache<String, Bitmap>(memorySize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    // API19
                    return value.getAllocationByteCount() / BYTE_UNIT;
                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) {
                    // API12
                    return value.getByteCount() / 1024;
                }
                // Bitmap所佔用的內存空間數等於Bitmap的每一行所佔用的空間數乘以Bitmap的行數
                return value.getRowBytes() * value.getHeight() / BYTE_UNIT;
            }
        };
    }

    public Bitmap get(String url) {
        return mImageCache.get(url);
    }

    public void put(String url, Bitmap bitmap) {
        mImageCache.put(url, bitmap);
    }
}
public class ImageLoader {
    // 封裝的緩存類
    private ImageCache mImageCache = new ImageCache();
    // 線程池,線程數量爲CPU的數量
    private ExecutorService mExecutorService;
    public ImageLoader() {
        mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    }

    /**
     * 加載圖片
     * @param url
     * @param imageView
     */
    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)) {
                    imageView.setImageBitmap(bitmap);
                }
                mImageCache.put(url, bitmap);
            }
        });
    }

    private Bitmap downloadImage(String url) {
        ...................
    }
}

1.2 讓程序更穩定、更靈活——開閉原則this

我的理解:「就是在設計類時,儘可能考慮其擴展性,保證在有需求變動時,能夠不修改原有代碼。通常是經過面向接口,經過多態方式實現」。url

軟件中的對象(類、模塊、函數等)應該對於擴展是開放的,可是對於修改是封閉的,這就是開放——關閉原則。也就是說,當軟件須要變化時,咱們應該儘可能經過擴展的方式來實現變化,而不是經過修改已有的代碼來實現。

上個例子中,圖片緩存咱們使用的ImageCache,主要是內存緩存。

這種問題就是,若是使用者想添加磁盤緩存,則須要更改現有代碼,不易於擴展。

改爲下面的方式,經過定義接口,調用setImageCache方式實現開發者擴展。

public interface ImageCache {
    void put(String key, Bitmap bitmap);
    Bitmap get(String key);
}
public class MemoryCache implements ImageCache {
    // 圖片緩存
    private LruCache<String, Bitmap> mImageCache;

    ..............

    @Override
    public Bitmap get(String url) {
        return mImageCache.get(url);
    }
    @Override
    public void put(String url, Bitmap bitmap) {
        mImageCache.put(url, bitmap);
    }
}
public class DiskCache implements ImageCache {
    @Override
    public void put(String key, Bitmap bitmap) {
    }
    @Override
    public Bitmap get(String key) {
        return null;
    }
}
public class ImageLoader {

    // 封裝的緩存類
    private ImageCache mImageCache = new MemoryCache();
    // 線程池,線程數量爲CPU的數量
    private ExecutorService mExecutorService;

    public ImageLoader() {
        mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    }

    public void setImageCache(ImageCache cache) {
        this.mImageCache = cache;
    }

    ......................
}

1.3 構建擴展性更好的系統——里氏替換原則

我們直接來看第二種定義:全部引用基類的地方必須能透明的使用其子類的對象。

面向對象的語言的三大特色是繼承、封裝、多態,里氏替換原則就是依賴於繼承、多態這兩大特性。

1.4 讓項目擁有變化的能力——依賴倒置原則

Java語言中的表現就是:模塊間的依賴經過抽象發生,實現類之間不發生直接的依賴關係,其依賴關係是經過接口或抽象類產生的。其實就是面向接口/抽象編程。

1.5 系統有更高的靈活性——接口隔離原則

定義是:客戶端不該該依賴它不須要的接口。接口隔離原則將很是龐大、臃腫的接口拆分紅更小的和更具體的接口。接口隔離原則的目的是系統解開耦合,從而容易重構、更改和從新部署。

1.6 更好的可擴展性——迪米特原則

通俗的講,一個類應該對本身須要耦合或調用的類知道的最少,類的內部如何實現與調用者不要緊,調用者只須要知道它須要的方法便可。

我的理解:「這個說的就是封裝,儘可能將相同的功能封裝在一個類,只對外暴露須要的接口,其餘的都不可見」。

終於整理完了第一章,這章說的核心思想就是面向對象的三大特色,繼承、封裝、多態。面向接口編程。

相關文章
相關標籤/搜索