六大設計原則之單一職責

1.前言

最近重溫了各種設計模式,順便寫了下筆記,分享出來給有緣的人,希望此係列文章能幫助到你們,那
將是我寫此係列文章的最有用的價值之一了。

學中作,做中學,這是學技術最好的方法之一,所以我幾乎都是通過例子來說明問題,這樣文章就有點
長,可是如果肯研究下,定會有收穫,如果只講理論原理,那似乎懂了,可是做的時候還是不會用,所以
踏踏實實的去動手是你進步的源泉了。本系列文章用到的例子是從書籍《Android 源碼設計模式解析與實戰》中獲取,在此感謝非常作者的汗水。若侵權則刪。由於表達的需要對這些例子做不同程度的修改。《Android 源碼設計模式解析與實戰》這本書很適合學習,大家可以直接去買來研究。

那爲啥要學設計模式呢?等你敲代碼到一定程度了自然就明白了。否則你學着會覺得沒有,這是我的真
實感受。在正式的學習經典的各種設計模式之前,我們先來看看設計模式的六大原則,這些原則都將會
體現在設計模式之中。

2.單一職責概念

單一職責,簡單來說就是一個類中一組相關性很高的函數、數據的封裝。用外行話來說,就是通常一個
類只負責一個功能。但是這個單一職責的界限並沒有那麼清晰的,需要自己的個人經驗來確定,不要陷
入去區分什麼纔算是單一職責的漩渦裏。等下我們會說明例子來說明這個問題。單一職責是優化代碼的
第一步。

3.菜鳥的寫法

  • 需求:圖片加載並顯示圖片,並將圖片緩存起來。
package com.study.demo.designdemo;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.LruCache;
import android.widget.ImageView;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 圖片加載類
 * Created by WKC on 2017/7/8.
 */

public class ImageLoader {
    //圖片緩存
    LruCache<String, Bitmap> mImageCache;
    //線程池,線程數量爲CUP的數量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().
            availableProcessors());
    //UI Handler
    Handler mUiHandler = new Handler(Looper.getMainLooper());

    //構造函數
    public ImageLoader() {
        initImageCache();
    }

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

    /**
     * 提供外部調用顯示圖片的函數
     *
     * @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);
            }
        });
    }

    /**
     * 在UI線程顯示圖片
     *
     * @param imageview
     * @param bitmap
     */
    private void updateImageview(final ImageView imageview, final Bitmap bitmap) {
        mUiHandler.post(new Runnable() {
            @Override
            public void run() {
                imageview.setImageBitmap(bitmap);
            }
        });
    }

    /**
     * 下載圖片
     *
     * @param imegeUrl
     * @return
     */
    private Bitmap downloadImage(String imegeUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imegeUrl);
            final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(conn.getInputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }
}

ok,寫好了,能順利的加載圖片,能顯示,也能緩存,似乎萬事大吉了。可是如果功能不斷的增多,這
個類就會變得越來越臃腫,代碼也越來越複雜,圖片加載系統就會越來越脆弱,那麼我們該怎麼做呢?
我們來分析下:這個類都做了哪些事?

  • 1.加載圖片;
  • 2.緩存圖片;
  • 3.顯示圖片。

主要做了這三件事。剛纔我們說了單一職責,就是一個類中應該是一組相關性很高的函數、數據的封
裝。而這個類顯然不符合這個要求。我們姑且把緩存的職責提出來,生成一個鮮明的單一職責的類–
緩存,這樣做的具體好處,會在下篇文章《六大設計原則之開閉原則》中有體現。現在我們把緩存提
取出來。當然我們也可以把下載的職責提出來,這個就留給你來練手了。
我們先看下UML圖:
這裏寫圖片描述

如圖所示:如果對UML圖還不瞭解,這篇文章可以幫助到你
http://www.uml.org.cn/oobject/201211231.asp

4.ImageLoader 代碼修改

package com.study.demo.designdemo;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.LruCache;
import android.widget.ImageView;

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 圖片加載類
 * Created by WKC on 2017/7/8.
 */

public class ImageLoader {
    //圖片緩存
    private ImageCache mImageCache = new ImageCache();
    //線程池,線程數量爲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);
            }
        });
    }

    /**
     * 在UI線程顯示圖片
     *
     * @param imageview
     * @param bitmap
     */
    private void updateImageview(final ImageView imageview, final Bitmap bitmap) {
        mUiHandler.post(new Runnable() {
            @Override
            public void run() {
                imageview.setImageBitmap(bitmap);
            }
        });
    }

    /**
     * 下載圖片
     *
     * @param imegeUrl
     * @return
     */
    private Bitmap downloadImage(String imegeUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imegeUrl);
            final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(conn.getInputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }

}

5.提取出來的緩存類

package com.study.demo.designdemo;

import android.graphics.Bitmap;
import android.util.LruCache;

/**
 * Created by WKC on 2017/7/8.
 */

public class ImageCache {
    //圖片緩存
    LruCache<String, Bitmap> mImageCache;

    public ImageCache() {
        initImageCache();
    }

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

    public void put(String url, Bitmap bitmap) {
        mImageCache.put(url, bitmap);
    }

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

這樣一拆分爲二,ImageLoader負責圖片加載並顯示的邏輯(讀者可以將加載和顯示繼續拆分,當做
練手),而ImageCache類只負責處理緩存的邏輯。這個代碼清晰了,如果需要修改某個邏輯時,如
對ImageCache類的邏輯進行修改,儘管在這個類中改好了,不必去打擾ImageLoader類。

從以上的例子中,再來說下單一職責的意思,所謂單一,就是其職能唯一或者類似的一個功能,但是
沒有嚴格的界限,每個人都有自己的理解,這需要經驗和具體的業務邏輯來定的。儘管如此,還是有
一些指導的原則,兩個功能完全不一樣的就不應該放在同一個類中。一個類中應該是相關性很高的函
數或者數據的封裝。

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