面向?qū)ο蟮脑瓌t之單一職責原則

單一職責的英文名稱是Single Responsibility Principle址遇,縮寫是SRP熄阻。

SRP的定義就是:就一個類而言,應(yīng)該僅有一個引起它變化的原因倔约。簡單說來秃殉,一個類中應(yīng)該是一組相關(guān)性很高的函數(shù)、數(shù)據(jù)的封裝浸剩。單一職責的劃分界限并不是總是那么清晰钾军,很多時候需要靠個人經(jīng)驗來界定。當然绢要,最大的問題就是堆職責的定義吏恭,什么是類的職責,以及怎么劃分類的職責重罪。

接下來以小明的工作過程為示例描述單一職責原則:

小明初入職場砸泛,在經(jīng)歷過一周的適應(yīng)期以及熟悉公司的產(chǎn)品、開發(fā)規(guī)范后蛆封,小民的開發(fā)工作就正式開始了唇礁。小民的主管是個工作經(jīng)驗豐富的技術(shù)專家,對于小民的工作并不是很滿意惨篱,尤其是小民最薄弱的面向?qū)ο笤O(shè)計盏筐,而Android開發(fā)又是使用Java語言,程序中的抽象砸讳、接口琢融、六大原則、23種設(shè)計模式等名詞把小民弄的暈頭轉(zhuǎn)向簿寂。于是漾抬,小民的主管決定先讓小民做一個小項目來鍛煉這方面的能力。

在經(jīng)過一番思考后常遂,主管挑選了使用范圍廣纳令、難度也適中的圖片加載器(ImageLoader)作為小民的訓(xùn)練項目。既然要訓(xùn)練小民的面向?qū)ο笤O(shè)計能力,那么久必須考慮到可擴展性平绩、靈活性圈匆,而檢測這一切是否符合需求的最好途徑就是開源。

小民是不服輸?shù)哪蟠疲鞴艿囊蠛芎唵卧咀∶駥崿F(xiàn)圖片加載,并且要將圖片緩存起來性湿。在分析了需求之后纬傲,小民放下心來,胸有成竹肤频,在經(jīng)歷了10分鐘的編碼之后叹括,小民寫下了如下代碼:

ImageLoader.java

package com.deason.library.srp;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.LruCache;
import android.widget.ImageView;

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

/**
 * 圖片加載類
 * Created by liuguoquan on 2016/3/13.
 */
public class ImageLoader {

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

    /**
     * 線程池,線程數(shù)量未CPU的數(shù)量
     */
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime()
            .availableProcessors());

    public ImageLoader() {

        initImageCache();
    }

    private void initImageCache() {

        //計算可使用的最大內(nèi)存
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        //取內(nèi)存的四分之一作為緩存
        final int cacheSize = maxMemory / 4;

        mImageCache = new LruCache<String, Bitmap>(cacheSize) {

            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight() / 1024;
            }
        };
    }

    /**
     * 顯示圖片
     * @param url
     * @param imageView
     */
    public void displayImage(final String url, final ImageView imageView) {

        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);
                }
            }
        });
    }

    /**
     * 下載圖片
     * @param url
     * @return
     */
    public Bitmap downloadImage(String imageUrl) {

        Bitmap bitmap = null;
        try {
            URL url = new URL(imageUrl);
            HttpURLConnection mConnection = (HttpURLConnection) url.openConnection();
            int code = mConnection.getResponseCode();

            if (200 == code) {
                bitmap = BitmapFactory.decodeStream(mConnection.getInputStream());
            }
            mConnection.disconnect();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }
}

隨后着裹,小民將代碼托管到GitHub上,伴隨著git push命令的完成米同,小民的ImageLoader 0.1版本就發(fā)布了骇扇。小民開始幻想著待會兒被主管稱贊。

在小民給主管報告了ImageLoader發(fā)布消息的幾分鐘后面粮,主管把小民叫到了會議室少孝,叼了小民一頓:“小民,你的ImageLoader耦合太嚴重啦熬苍!簡直就沒有設(shè)計可言稍走,更不要說是擴展性、靈活性了柴底。所有功能寫在一個類里面怎么行呢婿脸,這樣隨著功能的增多,ImageLoader類會越來越大柄驻。代碼也越來越復(fù)雜狐树,圖片的加載系統(tǒng)就會越來越弱...”,此時鸿脓,小民默默吞下了淚水抑钟。

主管最后說:“你還是把ImageLoader拆分一下,把各個功能獨立出來野哭,讓它們滿足單一職責原則在塔。”小民敏銳的捕捉到單一職責原則這個關(guān)鍵詞拨黔,他用百度蛔溃、Google搜索了資料之后,決定對ImageLoader進行一次重構(gòu)。這次小民認真地先畫了一幅UML類圖:

單一職責UML.png

ImageLoader修改后的代碼如下:

package com.deason.library.srp.refactor;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;

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

/**
 * 圖片加載類
 * Created by liuguoquan on 2016/3/14.
 */
public class ImageLoader {

    /**
     * 圖片緩存
     */
    ImageCache mImageCache = new ImageCache();

    /**
     * 線程池城榛,線程數(shù)量未CPU的數(shù)量
     */
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime()
            .availableProcessors());

    /**
     * 顯示圖片
     * @param url
     * @param imageView
     */
    public void displayImage(final String url, final ImageView imageView) {
        //首先檢查內(nèi)存是否存在
        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);
                }
                //將下載的圖片存入內(nèi)存
                mImageCache.put(url,bitmap);
            }
        });
    }

    /**
     * 下載圖片
     * @param url
     * @return
     */
    public Bitmap downloadImage(String imageUrl) {

        Bitmap bitmap = null;
        try {
            URL url = new URL(imageUrl);
            HttpURLConnection mConnection = (HttpURLConnection) url.openConnection();
            int code = mConnection.getResponseCode();

            if (200 == code) {
                bitmap = BitmapFactory.decodeStream(mConnection.getInputStream());
            }
            mConnection.disconnect();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }
}

并且添加類一個ImageCache類用于圖片緩存揪利,具體代碼如下:

package com.deason.library.srp.refactor;

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

/**
 * 處理圖片緩存
 * Created by liuguoquan on 2016/3/14.
 */
public class ImageCache {

    /**
     * 圖片緩存
     */
    LruCache<String, Bitmap> mImageCache;
    
    public ImageCache() {
        
        initImageCache();
    }

    private void initImageCache() {

        //計算可使用的最大內(nèi)存
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        //取內(nèi)存的四分之一作為緩存
        final int cacheSize = maxMemory / 4;

        mImageCache = new LruCache<String, Bitmap>(cacheSize) {

            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight() / 1024;
            }
        };
    }

    /**
     * 將圖片存入緩存
     * @param key
     * @param bitmap
     */
    public void put(String key,Bitmap bitmap) {
        
        mImageCache.put(key,bitmap);
    }

    /**
     * 取出緩存圖片
     * @param key
     * @return
     */
    public Bitmap get(String key) {
        return mImageCache.get(key);
    }
}

綜上所述,小民將ImageLoader一拆為二狠持,ImageLoader只負責圖片加載的邏輯疟位,而ImageCache只負責處理圖片緩存的邏輯,這樣ImageLoader的代碼量變少了喘垂,職責也清晰了甜刻;當與緩存相關(guān)的邏輯需要改變時,不需要修改ImageLoader類正勒,而圖片加載的邏輯需要修改時也不會影響到緩存處理邏輯得院。

從上述例子我們知道,單一職責所表達出來的用意就是“單一”兩個字章贞。如何劃分一個類祥绞、一個函數(shù)的職責,每個人都有自己的看法鸭限,這需要根據(jù)個人經(jīng)驗蜕径、具體的業(yè)務(wù)邏輯而定。但是败京,它也有一些基本的指導(dǎo)原則兜喻,例如,兩個完全不一樣的功能就不應(yīng)該方法一個類中赡麦,一個類中應(yīng)該是一組相關(guān)性很高的函數(shù)朴皆、數(shù)據(jù)的封裝。工程師可以不斷地審視自己的代碼泛粹,根據(jù)具體的業(yè)務(wù)遂铡、功能對類進行相應(yīng)的拆分,這是程序員優(yōu)化代碼邁出的第一步晶姊。

優(yōu)點

  • 類的復(fù)雜度降低忧便,實現(xiàn)什么職責都有清晰的定義。
  • 復(fù)雜性降低帽借,所以可讀性提高了珠增。
  • 可讀性提高了,所以可維護性提高了
  • 變更引起的風險降低砍艾,變更是必不可少的蒂教,如果接口的單一職責做得好,一個接口修改只對相應(yīng)的實現(xiàn)類有影響脆荷,對其他的接口無影響凝垛,這對系統(tǒng)的擴展性懊悯、維護性都有很大的幫助。

參考資料

《Android源碼設(shè)計模式》 何紅輝梦皮、關(guān)愛民 著

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末炭分,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子剑肯,更是在濱河造成了極大的恐慌捧毛,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件让网,死亡現(xiàn)場離奇詭異呀忧,居然都是意外死亡,警方通過查閱死者的電腦和手機溃睹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門而账,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人因篇,你說我怎么就攤上這事泞辐。” “怎么了竞滓?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵咐吼,是天一觀的道長。 經(jīng)常有香客問我虽界,道長汽烦,這世上最難降的妖魔是什么涛菠? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任莉御,我火速辦了婚禮,結(jié)果婚禮上俗冻,老公的妹妹穿的比我還像新娘礁叔。我一直安慰自己,他們只是感情好迄薄,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布琅关。 她就那樣靜靜地躺著,像睡著了一般讥蔽。 火紅的嫁衣襯著肌膚如雪涣易。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天冶伞,我揣著相機與錄音新症,去河邊找鬼。 笑死响禽,一個胖子當著我的面吹牛徒爹,可吹牛的內(nèi)容都是我干的荚醒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼隆嗅,長吁一口氣:“原來是場噩夢啊……” “哼界阁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起胖喳,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤泡躯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后禀晓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體精续,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年粹懒,在試婚紗的時候發(fā)現(xiàn)自己被綠了重付。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡凫乖,死狀恐怖确垫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情帽芽,我是刑警寧澤删掀,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站导街,受9級特大地震影響披泪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜搬瑰,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一款票、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泽论,春花似錦艾少、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鹦赎,卻和暖如春谍椅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背古话。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工雏吭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人煞额。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓思恐,卻偏偏與公主長得像沾谜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子胀莹,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 本文出自《Android源碼設(shè)計模式解析與實戰(zhàn)》中的第一章基跑。 1、優(yōu)化代碼的第一步——單一職責原則 單一職責原則的...
    MrSimp1e0閱讀 1,770評論 1 13
  • 前言最近同事買了本Android設(shè)計模式的書描焰,借來看看媳否,感覺還不錯,做一下筆記唄荆秦。有興趣的同學(xué)可以買原書看看:《A...
    肖丹晨閱讀 1,236評論 0 6
  • 開閉原則的英文全稱是Open Close Principle篱竭,縮寫是OCP,它是Java世界里最基礎(chǔ)的設(shè)計原則步绸,它...
    劉滌生閱讀 972評論 0 3
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,144評論 25 707
  • 聊聊抑郁癥【二】 我的發(fā)小陳女士就是曾經(jīng)的抑郁癥患者瓤介,我們從小在一個部隊大院里長大吕喘, 打小她就是我們小伙伴的領(lǐng)袖,...
    王一凡上海閱讀 310評論 0 1