再看Volley源码-设计模式

曾经在2年前写过一个Volley源码分析系列,感兴趣的可以看参考资料,时隔2年,随着对代码理解的不同,再看Volley源码时不再执着于细节,而是希望学习里面的设计模式,对缓存的设计,对单元测试的设计。今天重点分析下里面用到的设计模式,我们平时看设计模式的书,总觉得纸上得来终觉浅,例子过于简单,要用在真实项目中时就觉得无法下手,通过看Volley里面对设计模式的运用,对我们以后的设计会有一定的指导作用。笔者才疏学浅,欢迎各位看官批评指导。

策略模式

策略模式的定义(引用于《Java设计模式》)

定义一系列的算法,把它们一个个封装起来,并且使他们可互相替换。本模式使得算法可独立于使用它的客户而变化。

策略模式的优点(引用于《Java设计模式》)

  1. 上下文(Context)和具体策略(ConcreteStrategy)是松耦合关系。因此上下文只知道它要使用某一个实现Strategy接口类的实例,但不需要知道具体是哪个类。
  1. 策略模式满足“开-闭原则”。当增加新的具体策略时,不需要修改上下文类的代码,上下文就可以引用新的具体策略的实例。

适合策略模式的使用场景(引用于《Java设计模式》)

一个类定义了多种行为,并且这些行为在这个类的方法中以多个条件语句的形式出现,那么可以使用策略模式避免在类中使用大量的条件语句。

我们可以看到,在Volley中对于HttpStack的设计用到的就是策略模式。见下图:


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创建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设计模式》)

  1. 可以通过在抽象模板定义模板方法给出成熟的算法步骤,同时又不限制步骤的细节,具体模板实现算法细节不会改变整个算法的骨架。
  1. 在抽象模板模式中,可以通过钩子方法对某些步骤进行挂钩,具体模板通过钩子可以选择算法骨架中的某些步骤。

适合模板方法模式的使用场景(引用于《Java设计模式》)

  1. 设计者需要给出一个算法的固定步骤,并将某些步骤的具体实现留给子类来实现。
  1. 需要对代码进行重构,将各个子类公共行为抽取出来集中到一个共同的父类中以避免代码重复。

我们可以看到,在Volley中对于Request的设计用到的就是模板方法模式。见下图:


Volley模板方法模式UML.png

我们知道无论是请求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)

可以随意转发,也欢迎关注我的简书,我会坚持给大家带来分享。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容