再看Volley源碼-設(shè)計模式

曾經(jīng)在2年前寫過一個Volley源碼分析系列梳码,感興趣的可以看參考資料斜筐,時隔2年,隨著對代碼理解的不同邓夕,再看Volley源碼時不再執(zhí)著于細節(jié)刘莹,而是希望學(xué)習(xí)里面的設(shè)計模式,對緩存的設(shè)計焚刚,對單元測試的設(shè)計点弯。今天重點分析下里面用到的設(shè)計模式,我們平時看設(shè)計模式的書矿咕,總覺得紙上得來終覺淺抢肛,例子過于簡單狼钮,要用在真實項目中時就覺得無法下手,通過看Volley里面對設(shè)計模式的運用捡絮,對我們以后的設(shè)計會有一定的指導(dǎo)作用熬芜。筆者才疏學(xué)淺,歡迎各位看官批評指導(dǎo)福稳。

策略模式

策略模式的定義(引用于《Java設(shè)計模式》)

定義一系列的算法涎拉,把它們一個個封裝起來,并且使他們可互相替換的圆。本模式使得算法可獨立于使用它的客戶而變化鼓拧。

策略模式的優(yōu)點(引用于《Java設(shè)計模式》)

  1. 上下文(Context)和具體策略(ConcreteStrategy)是松耦合關(guān)系。因此上下文只知道它要使用某一個實現(xiàn)Strategy接口類的實例越妈,但不需要知道具體是哪個類噪矛。
  1. 策略模式滿足“開-閉原則”结序。當增加新的具體策略時陡舅,不需要修改上下文類的代碼玉掸,上下文就可以引用新的具體策略的實例。

適合策略模式的使用場景(引用于《Java設(shè)計模式》)

一個類定義了多種行為阎抒,并且這些行為在這個類的方法中以多個條件語句的形式出現(xiàn)赂韵,那么可以使用策略模式避免在類中使用大量的條件語句。

我們可以看到挠蛉,在Volley中對于HttpStack的設(shè)計用到的就是策略模式祭示。見下圖:


Volley策略模式UML.png

我們知道Android Framework里面同時包含HttpURLConnection和Apache HTTP Client 2套Http框架,HttpURLConnection相對輕量級谴古,也比較小质涛,而Apache HTTP Client接口多,比較大掰担,HttpURLConnection是最佳選擇汇陆,但在Android SDK小于9時,HttpURLConnection存在一些bug带饱,所以當Android SDK小于9時毡代,基于HttpClient創(chuàng)建HttpStack,否則基于HttpURLConnection創(chuàng)建HttpStack勺疼。具體可以看Android Developer Blog教寂。
所以Volley通過策略模式,在SDK不同的版本時選用不同的策略执庐,并且該策略也可以被替換酪耕,而不需要修改Volley類的代碼。

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

        String userAgent = "volley/0";
        try {
            String packageName = context.getPackageName();
            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
            userAgent = packageName + "/" + info.versionCode;
        } catch (NameNotFoundException e) {
        }

        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        Network network = new BasicNetwork(stack);

        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();

        return queue;
    }

我們可以看到轨淌,如果stack!=null時迂烁,直接使用stack看尼,如果stack==null,判斷Build.VERSION.SDK_INT>=9,如果成立,則使用HurlStack實例盟步,否則使用HttpClientStack實例藏斩。

模板方法模式

模板方法模式的定義(引用于《Java設(shè)計模式》)

定義一個操作中算法的骨架,而將一些步驟延遲到子類中却盘。模板方法使子類可以不改變一個算法結(jié)構(gòu)即可重定義該算法的某些特定步驟狰域。

模板方法模式的優(yōu)點(引用于《Java設(shè)計模式》)

  1. 可以通過在抽象模板定義模板方法給出成熟的算法步驟,同時又不限制步驟的細節(jié)谷炸,具體模板實現(xiàn)算法細節(jié)不會改變整個算法的骨架。
  1. 在抽象模板模式中禀挫,可以通過鉤子方法對某些步驟進行掛鉤旬陡,具體模板通過鉤子可以選擇算法骨架中的某些步驟。

適合模板方法模式的使用場景(引用于《Java設(shè)計模式》)

  1. 設(shè)計者需要給出一個算法的固定步驟语婴,并將某些步驟的具體實現(xiàn)留給子類來實現(xiàn)描孟。
  1. 需要對代碼進行重構(gòu),將各個子類公共行為抽取出來集中到一個共同的父類中以避免代碼重復(fù)砰左。

我們可以看到匿醒,在Volley中對于Request的設(shè)計用到的就是模板方法模式。見下圖:


Volley模板方法模式UML.png

我們知道無論是請求Image缠导,String廉羔,JsonObject還是JsonArray,唯一的區(qū)別就是對返回數(shù)據(jù)的解析方式(parseNetworkError)不同僻造,如果我們就可以通過模板方法模式對解析方式進行抽象憋他,讓子類分別實現(xiàn),這樣如果有新的對象返回需要解析髓削,只要新增子類實現(xiàn)對返回數(shù)據(jù)的解析方式就可以實現(xiàn)功能拓展竹挡。

public abstract class Request<T> implements Comparable<Request<T>> {
    ....代碼省略
    
    public Request(int method, String url, Response.ErrorListener listener) {
        mMethod = method;
        mUrl = url;
        mErrorListener = listener;
        setRetryPolicy(new DefaultRetryPolicy());

        mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
    }
   
    abstract protected Response<T> parseNetworkResponse(NetworkResponse response);

    protected VolleyError parseNetworkError(VolleyError volleyError) {
        return volleyError;
    }

    abstract protected void deliverResponse(T response);

}
public class StringRequest extends Request<String> {
    private final Listener<String> mListener;

    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }

    
    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

    @Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }

    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }
}

我們可以看到,在StringRequest里立膛,通過對parseNetworkResponse的實現(xiàn)揪罕,將返回數(shù)據(jù)解析成String格式。

總結(jié)

通過對策略模式和模板方法模式的分析宝泵,我們可以看到Volley里對設(shè)計模式的運用好啰,滿足了面向?qū)ο笤O(shè)計基本原則里面的面向抽象原則、開-閉原則和多用組合少用繼承原則儿奶。

參考資料

網(wǎng)絡(luò)通訊框架-Volley源碼分析(1)
網(wǎng)絡(luò)通訊框架-Volley源碼分析(2)
網(wǎng)絡(luò)通訊框架-Volley源碼分析(3)
網(wǎng)絡(luò)通訊框架-Volley源碼分析(4)

可以隨意轉(zhuǎn)發(fā)坎怪,也歡迎關(guān)注我的簡書,我會堅持給大家?guī)矸窒怼?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末廓握,一起剝皮案震驚了整個濱河市搅窿,隨后出現(xiàn)的幾起案子嘁酿,更是在濱河造成了極大的恐慌,老刑警劉巖男应,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闹司,死亡現(xiàn)場離奇詭異,居然都是意外死亡沐飘,警方通過查閱死者的電腦和手機游桩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耐朴,“玉大人借卧,你說我怎么就攤上這事∩盖停” “怎么了铐刘?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長影晓。 經(jīng)常有香客問我镰吵,道長,這世上最難降的妖魔是什么挂签? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任疤祭,我火速辦了婚禮,結(jié)果婚禮上饵婆,老公的妹妹穿的比我還像新娘勺馆。我一直安慰自己,他們只是感情好侨核,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布谓传。 她就那樣靜靜地躺著,像睡著了一般芹关。 火紅的嫁衣襯著肌膚如雪续挟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天侥衬,我揣著相機與錄音诗祸,去河邊找鬼。 笑死轴总,一個胖子當著我的面吹牛直颅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播怀樟,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼功偿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了往堡?” 一聲冷哼從身側(cè)響起械荷,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤共耍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吨瞎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痹兜,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年颤诀,在試婚紗的時候發(fā)現(xiàn)自己被綠了字旭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡崖叫,死狀恐怖遗淳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情心傀,我是刑警寧澤屈暗,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站剧包,受9級特大地震影響恐锦,放射性物質(zhì)發(fā)生泄漏往果。R本人自食惡果不足惜疆液,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望陕贮。 院中可真熱鬧堕油,春花似錦、人聲如沸肮之。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽戈擒。三九已至眶明,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筐高,已是汗流浹背搜囱。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留柑土,地道東北人蜀肘。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像稽屏,于是被迫代替她去往敵國和親扮宠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

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