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

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

策略模式

策略模式的定義(引用于《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),將各個子類公共行為抽取出來集中到一個共同的父類中以避免代碼重復。

我們可以看到,在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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容