曾经在2年前写过一个Volley源码分析系列,感兴趣的可以看参考资料,时隔2年,随着对代码理解的不同,再看Volley源码时不再执着于细节,而是希望学习里面的设计模式,对缓存的设计,对单元测试的设计。今天重点分析下里面用到的设计模式,我们平时看设计模式的书,总觉得纸上得来终觉浅,例子过于简单,要用在真实项目中时就觉得无法下手,通过看Volley里面对设计模式的运用,对我们以后的设计会有一定的指导作用。笔者才疏学浅,欢迎各位看官批评指导。
策略模式
策略模式的定义(引用于《Java设计模式》)
定义一系列的算法,把它们一个个封装起来,并且使他们可互相替换。本模式使得算法可独立于使用它的客户而变化。
策略模式的优点(引用于《Java设计模式》)
- 上下文(Context)和具体策略(ConcreteStrategy)是松耦合关系。因此上下文只知道它要使用某一个实现Strategy接口类的实例,但不需要知道具体是哪个类。
- 策略模式满足“开-闭原则”。当增加新的具体策略时,不需要修改上下文类的代码,上下文就可以引用新的具体策略的实例。
适合策略模式的使用场景(引用于《Java设计模式》)
一个类定义了多种行为,并且这些行为在这个类的方法中以多个条件语句的形式出现,那么可以使用策略模式避免在类中使用大量的条件语句。
我们可以看到,在Volley中对于HttpStack的设计用到的就是策略模式。见下图:
我们知道Android Framework里面同时包含HttpURLConnection和Apache HTTP Client 2套Http框架,HttpURLConnection相对轻量级,也比较小,而Apache HTTP Client接口多,比较大,HttpURLConnection是最佳选择,但在Android SDK小于9时,HttpURLConnection存在一些bug,所以当Android SDK小于9时,基于HttpClient创建HttpStack,否则基于HttpURLConnection创建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设计模式》)
定义一个操作中算法的骨架,而将一些步骤延迟到子类中。模板方法使子类可以不改变一个算法结构即可重定义该算法的某些特定步骤。
模板方法模式的优点(引用于《Java设计模式》)
- 可以通过在抽象模板定义模板方法给出成熟的算法步骤,同时又不限制步骤的细节,具体模板实现算法细节不会改变整个算法的骨架。
- 在抽象模板模式中,可以通过钩子方法对某些步骤进行挂钩,具体模板通过钩子可以选择算法骨架中的某些步骤。
适合模板方法模式的使用场景(引用于《Java设计模式》)
- 设计者需要给出一个算法的固定步骤,并将某些步骤的具体实现留给子类来实现。
- 需要对代码进行重构,将各个子类公共行为抽取出来集中到一个共同的父类中以避免代码重复。
我们可以看到,在Volley中对于Request的设计用到的就是模板方法模式。见下图:
我们知道无论是请求Image,String,JsonObject还是JsonArray,唯一的区别就是对返回数据的解析方式(parseNetworkError)不同,如果我们就可以通过模板方法模式对解析方式进行抽象,让子类分别实现,这样如果有新的对象返回需要解析,只要新增子类实现对返回数据的解析方式就可以实现功能拓展。
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的实现,将返回数据解析成String格式。
总结
通过对策略模式和模板方法模式的分析,我们可以看到Volley里对设计模式的运用,满足了面向对象设计基本原则里面的面向抽象原则、开-闭原则和多用组合少用继承原则。
参考资料
网络通讯框架-Volley源码分析(1)
网络通讯框架-Volley源码分析(2)
网络通讯框架-Volley源码分析(3)
网络通讯框架-Volley源码分析(4)
可以随意转发,也欢迎关注我的简书,我会坚持给大家带来分享。