Android 設計模式之面向對象的六大原則

在平常開發過程當中時常須要用到設計模式,可是設計模式有23種,如何將這些設計模式瞭然於胸而且能在實際開發過程當中應用得駕輕就熟呢?和我一塊兒跟着《Android源碼設計模式解析與實戰》一書邊學邊應用吧!

設計模式系列文章


今天咱們要講的是面向對象的六大原則

單一職責原則

就一個類而言,應該僅有一個引發它變化的緣由。簡單來講,一個類中應該是一組相關性很高的函數、數據的封裝。
  • 咱們在App中每每會用到不少的公共方法,好比獲取系統的時間,這個功能可能在App中的不少地方都要用到。這個時候咱們通常會單獨寫個工具類TimeUtils,把處理時間有關的方法都放到這個類裏面,這樣就能減小重複代碼,App的結構會更加清晰。當須要添加其餘跟時間有關的方法時,就能夠都加到這個TimeUtils類裏面,這就是咱們平時遵循的單一職責原則。

開閉原則

軟件中的對象(類、模塊、函數等),應該對於擴展是開放的,而對於修改是封閉的。
  • 咱們在軟件開發過程當中就要考慮到後續的擴展和修改。好比說,咱們在開發一款相似於universal-image-loader的圖片加載框架,可能一開始咱們的功能比較簡單,圖片緩存只有內存緩存。當咱們新版本須要添加SD卡緩存時,就要注意儘量的減小對原來代碼的修改,由於這樣極可能會引入新的bug。而要作到開閉原則,通常有2種途徑,一是經過繼承原有的類;二是經過抽象和接口。後面咱們會拿書中的圖片加載框架來具體說明。

里氏替換原則

全部引用基類的地方必須能透明的使用其子類。通俗的說,就是隻要父類能出現的地方子類就能夠出現,並且替換爲子類之後不會出現任何錯誤或異常。反過來就不行了,子類出現的地方父類不必定能適應。
  • 要實現里氏替換原則,通常須要一個抽象的父類,父類中定義了子類的公共方法,子類繼承或是實現父類之後擴展不一樣的功能,這樣以來能夠實現根據不一樣的須要來應用對應的子類,從而達到應用不一樣的功能的目的,程序的擴展性大大加強。同時這也體現了開閉原則,即當須要增長新功能時,只要繼承或實現父類,實現新增的功能高就達到了擴展的目的,而不是直接修改原來的代碼,也即對擴展開放,對修改封閉。

依賴倒置原則

依賴倒置原則在Java中的表現就是:模塊間的依賴經過抽象發生,實現類之間不發生直接的依賴關係,其依賴關係是經過接口或抽象類產生的。
  • 一句話就是依賴抽象而不依賴具體的實現。好比上面咱們說開發一個圖片加載框架,那咱們確定會根據單一職責原則劃分不一樣的模塊,好比網絡加載模塊,圖片緩存模塊,以及主模塊等。主模塊確定會調用圖片緩存模塊,若是咱們調用的是圖片緩存模塊的具體實現,那麼當咱們修改圖片模塊時就極可能要對應修改主模塊,這就是耦合了。一個比較好的作法是將圖片緩存模塊抽象出來,而主模塊調用這個抽象便可,這樣也就是依賴抽象了。

接口隔離原則

類間的依賴關係應該創建在最小的接口上。
  • 接口隔離原則就是讓客戶端依賴的接口儘量的小。就是在上面提到的依賴倒置(依賴抽象而不是實現)原則的基礎上,增長一個最小化依賴的原則。說白了就是在依賴接口的基礎上依賴儘量少的接口。
  • 這裏舉個例子:
<!--將圖片寫入SD卡-->
public void put(String url, Bitmap bitmap) {
    FileOutputStream fileOutputStream = null;
    try {
        fileOutputStream = new FileOutputStream(SDPath+url);
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
    }catch (FileNotFoundException e) {
        e.printStackTrace();
    }finally {
        CloseUtils.closeQuietly(fileOutputStream);
    }
}

<!--關閉工具類-->
public final class CloseUtils {

    private CloseUtils() { }

    /**
     * 關閉Closeable對象
     * @param closeable
     */
    public static void closeQuietly(Closeable closeable) {
        if (null != closeable) {
            try {
                closeable.close();
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
複製代碼
  • 上述例子中的CloseUtils的closeQuietly方法的原理就是依賴於Closeable抽象而不是具體實現(依賴倒置原則),而且創建在最小化依賴的基礎上,它只要知道這個對象是能夠關閉的就好了,其餘的一律不用關心,這就是接口隔離原則

迪米特原則

一個對象應該對其餘的對象有最少的瞭解
  • 通俗的講,一個類應該對本身須要耦合或調用的類知道得最少,調用者或是依賴者只要知道它須要的方法便可。要作到這個原則,須要咱們對各個模塊之間的功能進行很好的區分和分配,把相互之間的依賴和耦合減到最少。

以上就是面向對象的六大原則。

下面咱們經過書中的圖片加載框架ImageLoader的例子介紹這些原則的具體應用。
  • 首先是ImageLoader類
public class ImageLoader {
    // 圖片緩存,依賴接口,而不是具體實現
    // 若是改成MemoryCache mImageCache = new MemoryCache();就不能定製圖片緩存的實現,擴展性大大下降,耦合度也會大大提升
    ImageCache mImageCache = new MemoryCache();
    // 線程池,線程數量爲CPU的數量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    // 注入緩存,對擴展開放,對修改關閉
    public void setImageCache(ImageCache cache) {
        mImageCache = cache;
    }

    /**
     * 顯示圖片
     * @param imageUrl
     * @param imageView
     */
    public void displayImage(String imageUrl, ImageView imageView) {
        Bitmap bitmap = mImageCache.get(imageUrl);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }
        // 圖片沒有緩存,提交到線程池下載
        submitLoadRequest(imageUrl, imageView);
    }

    /**
     * 下載圖片
     * @param imageUrl
     * @param imageView
     */
    private void submitLoadRequest(final String imageUrl, final ImageView imageView) {
        imageView.setTag(imageUrl);
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = downloadImage(imageUrl);
                if (bitmap == null) {
                    return;
                }
                if (imageUrl.equals(imageView.getTag())) {
                    imageView.setImageBitmap(bitmap);
                }
                mImageCache.put(imageUrl, bitmap);
            }
        });
    }

    /**
     * 下載圖片
     * @param imageUrl
     * @return
     */
    private Bitmap downloadImage(String imageUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imageUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(connection.getInputStream());
            connection.disconnect();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }
複製代碼
  • 而後是圖片緩存接口以及內存緩存
<!--圖片緩存接口-->
public interface ImageCache {
    public Bitmap get(String url);
    public void put(String url, Bitmap bitmap);
}

<!--內存緩存的實現-->
public class MemoryCache implements ImageCache{
    private LruCache<String, Bitmap> mMemoryCache;

    public MemoryCache() {
        //初始化LRU緩存
        initImageCache();
    }
    
    private void initImageCache() {
        // 計算可以使用的最大內存
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);
        // 取四分之一的可用內存做爲緩存
        final int cacheSize = maxMemory / 4;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };
    }


    @Override
    public Bitmap get(String url) {
        return mMemoryCache.get(url);
    }

    @Override
    public void put(String url, Bitmap bitmap) {
        mMemoryCache.put(url, bitmap);
    }
}
複製代碼
ImageLoader類中的邏輯比較直觀,六大原則的體現主要在圖片緩存的處理上。
  • 將圖片緩存單獨出去而不是寫在ImageLoader一塊兒,體現了單一職責原則
  • ImageLoader中依賴的是圖片緩存的接口而不是具體的實現,體現了開閉原則、里氏替換原則和依賴倒置原則。
  • ImageLoader類和MemoryCache類之間只依賴ImageCache接口,也能夠說體現了接口隔離原則
  • ImageLoader類只須要知道MemoryCache類的put方法和get方法,其餘的實現一律無論,也體現了迪米特原則
固然以上的ImageLoader還只是第一版,還有不少有待優化的地方,好比能夠把下載的邏輯單獨出去,能夠增長更多的定製功能等。

六大原則能讓咱們的代碼結構更加合理,擴展性更好,讓程序更穩定靈活。這些原則每每都是共同做用的,好比上面例子中的CloseUtils知足了單一職責原則,closeQuietly方法運用了依賴倒置,而且遵循接口隔離原則。趕忙用六大原則優化你的代碼吧!!!


歡迎掃碼關注公衆號,我們一塊兒學習成長吧 設計模式

AntDream
相關文章
相關標籤/搜索