曾經(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è)計模式》)
- 上下文(Context)和具體策略(ConcreteStrategy)是松耦合關(guān)系。因此上下文只知道它要使用某一個實現(xiàn)Strategy接口類的實例越妈,但不需要知道具體是哪個類噪矛。
- 策略模式滿足“開-閉原則”结序。當增加新的具體策略時陡舅,不需要修改上下文類的代碼玉掸,上下文就可以引用新的具體策略的實例。
適合策略模式的使用場景(引用于《Java設(shè)計模式》)
一個類定義了多種行為阎抒,并且這些行為在這個類的方法中以多個條件語句的形式出現(xiàn)赂韵,那么可以使用策略模式避免在類中使用大量的條件語句。
我們可以看到挠蛉,在Volley中對于HttpStack的設(shè)計用到的就是策略模式祭示。見下圖:
我們知道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è)計模式》)
- 可以通過在抽象模板定義模板方法給出成熟的算法步驟,同時又不限制步驟的細節(jié)谷炸,具體模板實現(xiàn)算法細節(jié)不會改變整個算法的骨架。
- 在抽象模板模式中禀挫,可以通過鉤子方法對某些步驟進行掛鉤旬陡,具體模板通過鉤子可以選擇算法骨架中的某些步驟。
適合模板方法模式的使用場景(引用于《Java設(shè)計模式》)
- 設(shè)計者需要給出一個算法的固定步驟语婴,并將某些步驟的具體實現(xiàn)留給子類來實現(xiàn)描孟。
- 需要對代碼進行重構(gòu),將各個子類公共行為抽取出來集中到一個共同的父類中以避免代碼重復(fù)砰左。
我們可以看到匿醒,在Volley中對于Request的設(shè)計用到的就是模板方法模式。見下圖:
我們知道無論是請求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>