前言
最近学校的设计模式课程设计要求我们选一个框架进行设计模式的分析,结果老师给的题目都是跟Android毫无瓜葛的。这时候我想起了Retrofit,毕竟有一段时间曾经了解过,据说是设计模式的教科书。所以秉着作死的心态向老师自定义了题目(真™刺激呀),于是乎在一顿折腾后坎坷的看出大致的模式。这篇文章是一个学生党对框架设计模式的分析,可能有错误的地方,希望能得到大家的指导。
关于Retrofit
Retrofit是一个好东西,出自square公司之手,说到square公司自然不得不提一个大神,那就是jack wharton,很多现在流行的轮子都是他参与造的,比如这次的Retrofit啦,Rxjava啦,黄油刀啦,OkHttp啦等等。这里关于Retrofit的介绍就详细讲了,里有很多文章,大家可以搜搜。
Retrofit的基本使用
笔者用的是Kotlin语言,短短主题五行代码就可以简单的完成一次网络请求。与OkHttp相比呢也不用自己来写请求体和回应体的解析。或许这就是Retrofit受欢迎的原因之一吧。
使用步骤描述:
1.先定义一个接口,使用@Get,@Path等注解定义,如上图。
2.创建一个Retrofit对象,使用其内部Builder类来进行对Retrofit对象的配置
3.创建一个Call(请求体),使用Retrofit的create方法传入定义好的接口
4.调用已创建Call的方法(接口内的方法),传入用户想要的参数
5.执行Call,返回一个回应体
6.对回应体进行一系列用户需要的操作
完
框架设计模式分析
1.分析框架的初步运行流程
总结:Retofit类负责配置,其中会涉及到一些其他的类,如工厂什么的,但是最后Call的返回逻辑在ServiceMethod类中,ServiceMethod是一个很关键的类,涉及到了具体运行原理和调用,这里我们只做设计模式分析,就不深入啦。
2.深入分析
我们一步一步来,最后面会放上类图,咱们先不急。
第一步:配置Retrofit
从名字我们就可以看出,毫无疑问配置这一部用了个构造者模式呀(Builder),学生党我暗自偷笑。是不是不用看啦??不急不急,我们还是进去看一看。
可以看到Retrofit类中包含了一个Builder类,点进去一看,是一个静态类(这里就不放图了),再看看上图中若干个addXXX方法和一些其他方法均是返回Builder自身的,还有一个build()方法返回一个Retrofit的,这一罗列,哎呀妈呀果真是构造者方法,错不了错不了。
知道这里是使用了构造方法后,那有什么用呀?因为网络请求要配置各种参数,而有些参数是不必要的,如果不使用构造者模式,那Retrofit就必须得写一大串参数类型不同的构造器,用于对参数传入,那代码就非常的不雅,可读性也很差,所以就用了个Builder啦。
等等!就这么完了吗?但是你仔细看一下参数,addXXXFactory?里面竟然维护了一个叫XXFactory的列表?这难道是...抽象工厂??工厂方法??(学生党暗自偷笑,这作业稳了)我们先点进去一探究竟
经过一顿分析,可以非常确定这是一个抽象工厂和工厂方法的设计模式。CallAdapter.Factory为抽象工厂,CallAdapter为工厂的产品接口,而上图中的ExecutorCallAdapterFactory和DefaultCallAdapterFactory为他的实现类,其中ExecutorCallAdapterFactory中的内部类ExecutorCallBackCall为该工厂的产品,实现了Call接口。
等等!!你说ExecutorCallBackCall是工厂的产品?但无论是从名字上来看还是从接口上来看都不应该啊?因为他实现了Call接口,但没有实现产品应该实现的CallAdapter接口啊?
的确如此,但是。CallAdapter这名字不觉的很熟悉吗?没错Adapter,其实这里还用了一个适配模式,而这整一个工厂最终目的是返回一个适配器,基于某种兼容目的的适配器。
也就是说,产品其实是他匿名实现的CallAdapter,只是里面返回了一个ExecutorCallbackCall,这里利用创建工厂实现时传进来的一个实现了Java Executor接口的类,来进行对参数call的一个适配。
这里你会很好奇为什么ExecutorCallbackCall传进一个实现了Java Executor接口的类和一个call做为参数。
经过一番查看,先看看Call接口,ExecutorCallbackCall实现的Call接口和在适配方法中传进来的call参数实现的接口是一毛一样的!!而从类的结构来看,看属性delegate的调用。是的,其实该类的实现实际是交给(委托)给传进来的call参数(delegate属性)实现的,然后只是在使用另一个传进来的参数(callbackExecutor)环境中运行【看红线部分】。
也就是说。这个类是包装用的,实现的功能都是委托给call的。装饰器模式,不知道为何脑内浮现出这几个大字...
到此。我们连同装饰器,适配器整个抽象工厂,工厂方法分析完毕。这一层套一层,环环相扣,这让人颤栗啊,这™设计的太可怕的。多种设计模式的运用巧而不显冗杂,岂是我等能做到的!
还有一点要提,我们上课的时候喜欢把产品接口和抽象方法分开两个文件来写,但是这里是将抽象工厂写在了接口中。这很巧妙,因为这样使用的时候就非常明显【接口.抽象工厂】,一看就知道这工厂的产品和抽象。而且合在一起写也不用为命令感到烦恼。
总结一下:
我们从配置Retrofit为出发点,分析出了抽象工厂,工厂方法,单例,适配器,装饰器五种模式。可能就要问了,在哪里开始初始化了这个工厂?有两个地方
第一:通过Retofit中Builder的addCallAdapterFactory方法
第二:在我们调出Builder时,在无参构造器中,通过调用Platform.get()来初始化
What??Platform啥玩意?不用担心,源代码作者命名非常好,看名字大概就知道,是平台嘛,平台类是干嘛?
可以看出里面有两个内部静态类Java8和Android,还有两个defaultCallXXX的方法。到这里我们就可以结合适配器工厂和这里两个内部类的类名揣测出适配器是为了适配不同平台下的运行环境。Android有主线程(UI线程)而Java8则没有
这里框架就默认实现了两个平台。Platform类是可继承的,如果没有我们的需求就可以继承该类并实现自己的平台。
说要这里第一步配置就说完了,但是返回文中刚开始的地方,你会看到一个叫converterFactories的列表,这是一个converter工厂的列表,也是一个抽象工厂,这里就不详细说了,下面只给出类图,仅供大家参考
第二步:使用create方法传入接口,返回call
老规矩咯,点进去看看..
哎呀woc这什么东西。学生党看不懂,但是我们从Proxy这个名字可以看出,或许是个代理模式。经过资料查询后发现,这里是实现了Java中自带的动态代理。何为动态代理?就是在创建一个实例前,你或许要对这个实例动点手脚,而我们又不能在创建实例的是时候决定要动点什么手脚,所以先创建一个代理对象给你用着先,等你动完该动的手脚,才开始真正创建实例。也就是说,这里我们调用create方法本来是要返回一个call对象的,但是!我们要在传入需要的参数后才能真正创建call对象,也就是说在文中开始的第三步后才真正创建了一个call。
这里笔者就不探究动态代理如何实现的了(因为我也不会呀..)我们继续往下看,这动态代理内的代码
首先是调用了自身的loadServiceMethod方法,加载(获得)了一个ServiceMethod类,然后通过其创建了一个OkHttpCall实例,最后调用了serviceMethod实例中的一个callAdapter对象中的adapt方法,最终返回了一个call。为这个adapt方法就是之前分析中的工厂方法产品类的方法。
查看一下loadServiceMethod的代码
看来Retrofit内部保存了一个ServiceMethod的map(key是从接口中反射出来的方法),没找到的话就通过ServiceMethod的Builder构造一个(又是一个构造者方法),然后存进map中。而ServiceMethod中就会创建callAdapter,这其中又会跳回Retrofit中,从其维护的callAdapter工厂列表中查找..挺复杂的,就不说啦,毕竟原则是设计模式分析嘛。
再看看用serviceMethod构造的OkHttpCall
其实现了call接口,就是之前在分析适配器讲到的call接口一样的。里面包含了一个另一个call接口:rawCall,这个rawCall与上面的call不同,因为他是来自okhttp3的。也就是这里的OkHttpCall其实就是包装了okhttp3中的call,功能的调用都委托给他..又一个装饰器。
总结
到这里,Retrofit中用到的设计模式我能发现到的,基本分析完了,里面运用了抽象工厂,工厂方法,单例,装饰器,适配器,动态代理。光是配置这一步就用了前六种设计模式..太可怕了..
希望你们能看懂...虚心接受指导和更正..