前言
最近學(xué)校的設(shè)計(jì)模式課程設(shè)計(jì)要求我們選一個(gè)框架進(jìn)行設(shè)計(jì)模式的分析,結(jié)果老師給的題目都是跟Android毫無瓜葛的。這時(shí)候我想起了Retrofit,畢竟有一段時(shí)間曾經(jīng)了解過,據(jù)說是設(shè)計(jì)模式的教科書。所以秉著作死的心態(tài)向老師自定義了題目(真?刺激呀),于是乎在一頓折騰后坎坷的看出大致的模式。這篇文章是一個(gè)學(xué)生黨對框架設(shè)計(jì)模式的分析,可能有錯(cuò)誤的地方,希望能得到大家的指導(dǎo)。
關(guān)于Retrofit
Retrofit是一個(gè)好東西,出自square公司之手,說到square公司自然不得不提一個(gè)大神,那就是jack wharton,很多現(xiàn)在流行的輪子都是他參與造的,比如這次的Retrofit啦,Rxjava啦,黃油刀啦,OkHttp啦等等。這里關(guān)于Retrofit的介紹就詳細(xì)講了,簡書里有很多文章,大家可以搜搜。
Retrofit的基本使用


筆者用的是Kotlin語言,短短主題五行代碼就可以簡單的完成一次網(wǎng)絡(luò)請求。與OkHttp相比呢也不用自己來寫請求體和回應(yīng)體的解析?;蛟S這就是Retrofit受歡迎的原因之一吧。
? 使用步驟描述:
1.先定義一個(gè)接口,使用@Get,@Path等注解定義,如上圖。
2.創(chuàng)建一個(gè)Retrofit對象,使用其內(nèi)部Builder類來進(jìn)行對Retrofit對象的配置
3.創(chuàng)建一個(gè)Call(請求體),使用Retrofit的create方法傳入定義好的接口
4.調(diào)用已創(chuàng)建Call的方法(接口內(nèi)的方法),傳入用戶想要的參數(shù)
5.執(zhí)行Call,返回一個(gè)回應(yīng)體
6.對回應(yīng)體進(jìn)行一系列用戶需要的操作
完
框架設(shè)計(jì)模式分析
1.分析框架的初步運(yùn)行流程


總結(jié):Retofit類負(fù)責(zé)配置,其中會涉及到一些其他的類,如工廠什么的,但是最后Call的返回邏輯在ServiceMethod類中,ServiceMethod是一個(gè)很關(guān)鍵的類,涉及到了具體運(yùn)行原理和調(diào)用,這里我們只做設(shè)計(jì)模式分析,就不深入啦。
2.深入分析
我們一步一步來,最后面會放上類圖,咱們先不急。

第一步:配置Retrofit
從名字我們就可以看出,毫無疑問配置這一部用了個(gè)構(gòu)造者模式呀(Builder),學(xué)生黨我暗自偷笑。是不是不用看啦??不急不急,我們還是進(jìn)去看一看。

可以看到Retrofit類中包含了一個(gè)Builder類,點(diǎn)進(jìn)去一看,是一個(gè)靜態(tài)類(這里就不放圖了),再看看上圖中若干個(gè)addXXX方法和一些其他方法均是返回Builder自身的,還有一個(gè)build()方法返回一個(gè)Retrofit的,這一羅列,哎呀媽呀果真是構(gòu)造者方法,錯(cuò)不了錯(cuò)不了。
知道這里是使用了構(gòu)造方法后,那有什么用呀?因?yàn)榫W(wǎng)絡(luò)請求要配置各種參數(shù),而有些參數(shù)是不必要的,如果不使用構(gòu)造者模式,那Retrofit就必須得寫一大串參數(shù)類型不同的構(gòu)造器,用于對參數(shù)傳入,那代碼就非常的不雅,可讀性也很差,所以就用了個(gè)Builder啦。
等等!就這么完了嗎?但是你仔細(xì)看一下參數(shù),addXXXFactory?里面竟然維護(hù)了一個(gè)叫XXFactory的列表?這難道是...抽象工廠??工廠方法??(學(xué)生黨暗自偷笑,這作業(yè)穩(wěn)了)我們先點(diǎn)進(jìn)去一探究竟





經(jīng)過一頓分析,可以非常確定這是一個(gè)抽象工廠和工廠方法的設(shè)計(jì)模式。CallAdapter.Factory為抽象工廠,CallAdapter為工廠的產(chǎn)品接口,而上圖中的ExecutorCallAdapterFactory和DefaultCallAdapterFactory為他的實(shí)現(xiàn)類,其中ExecutorCallAdapterFactory中的內(nèi)部類ExecutorCallBackCall為該工廠的產(chǎn)品,實(shí)現(xiàn)了Call接口。
等等!!你說ExecutorCallBackCall是工廠的產(chǎn)品?但無論是從名字上來看還是從接口上來看都不應(yīng)該???因?yàn)樗麑?shí)現(xiàn)了Call接口,但沒有實(shí)現(xiàn)產(chǎn)品應(yīng)該實(shí)現(xiàn)的CallAdapter接口???
的確如此,但是。CallAdapter這名字不覺的很熟悉嗎?沒錯(cuò)Adapter,其實(shí)這里還用了一個(gè)適配模式,而這整一個(gè)工廠最終目的是返回一個(gè)適配器,基于某種兼容目的的適配器。

也就是說,產(chǎn)品其實(shí)是他匿名實(shí)現(xiàn)的CallAdapter,只是里面返回了一個(gè)ExecutorCallbackCall,這里利用創(chuàng)建工廠實(shí)現(xiàn)時(shí)傳進(jìn)來的一個(gè)實(shí)現(xiàn)了Java Executor接口的類,來進(jìn)行對參數(shù)call的一個(gè)適配。
這里你會很好奇為什么ExecutorCallbackCall傳進(jìn)一個(gè)實(shí)現(xiàn)了Java Executor接口的類和一個(gè)call做為參數(shù)。


經(jīng)過一番查看,先看看Call接口,ExecutorCallbackCall實(shí)現(xiàn)的Call接口和在適配方法中傳進(jìn)來的call參數(shù)實(shí)現(xiàn)的接口是一毛一樣的??!而從類的結(jié)構(gòu)來看,看屬性delegate的調(diào)用。是的,其實(shí)該類的實(shí)現(xiàn)實(shí)際是交給(委托)給傳進(jìn)來的call參數(shù)(delegate屬性)實(shí)現(xiàn)的,然后只是在使用另一個(gè)傳進(jìn)來的參數(shù)(callbackExecutor)環(huán)境中運(yùn)行【看紅線部分】。
也就是說。這個(gè)類是包裝用的,實(shí)現(xiàn)的功能都是委托給call的。裝飾器模式,不知道為何腦內(nèi)浮現(xiàn)出這幾個(gè)大字...
到此。我們連同裝飾器,適配器整個(gè)抽象工廠,工廠方法分析完畢。這一層套一層,環(huán)環(huán)相扣,這讓人顫栗啊,這?設(shè)計(jì)的太可怕的。多種設(shè)計(jì)模式的運(yùn)用巧而不顯冗雜,豈是我等能做到的!
還有一點(diǎn)要提,我們上課的時(shí)候喜歡把產(chǎn)品接口和抽象方法分開兩個(gè)文件來寫,但是這里是將抽象工廠寫在了接口中。這很巧妙,因?yàn)檫@樣使用的時(shí)候就非常明顯【接口.抽象工廠】,一看就知道這工廠的產(chǎn)品和抽象。而且合在一起寫也不用為命令感到煩惱。
總結(jié)一下:

我們從配置Retrofit為出發(fā)點(diǎn),分析出了抽象工廠,工廠方法,單例,適配器,裝飾器五種模式??赡芫鸵獑柫?,在哪里開始初始化了這個(gè)工廠?有兩個(gè)地方
第一:通過Retofit中Builder的addCallAdapterFactory方法

第二:在我們調(diào)出Builder時(shí),在無參構(gòu)造器中,通過調(diào)用Platform.get()來初始化


What??Platform啥玩意?不用擔(dān)心,源代碼作者命名非常好,看名字大概就知道,是平臺嘛,平臺類是干嘛?
可以看出里面有兩個(gè)內(nèi)部靜態(tài)類Java8和Android,還有兩個(gè)defaultCallXXX的方法。到這里我們就可以結(jié)合適配器工廠和這里兩個(gè)內(nèi)部類的類名揣測出適配器是為了適配不同平臺下的運(yùn)行環(huán)境。Android有主線程(UI線程)而Java8則沒有


這里框架就默認(rèn)實(shí)現(xiàn)了兩個(gè)平臺。Platform類是可繼承的,如果沒有我們的需求就可以繼承該類并實(shí)現(xiàn)自己的平臺。
說要這里第一步配置就說完了,但是返回文中剛開始的地方,你會看到一個(gè)叫converterFactories的列表,這是一個(gè)converter工廠的列表,也是一個(gè)抽象工廠,這里就不詳細(xì)說了,下面只給出類圖,僅供大家參考

第二步:使用create方法傳入接口,返回call

老規(guī)矩咯,點(diǎn)進(jìn)去看看..

哎呀woc這什么東西。學(xué)生黨看不懂,但是我們從Proxy這個(gè)名字可以看出,或許是個(gè)代理模式。經(jīng)過資料查詢后發(fā)現(xiàn),這里是實(shí)現(xiàn)了Java中自帶的動態(tài)代理。何為動態(tài)代理?就是在創(chuàng)建一個(gè)實(shí)例前,你或許要對這個(gè)實(shí)例動點(diǎn)手腳,而我們又不能在創(chuàng)建實(shí)例的是時(shí)候決定要?jiǎng)狱c(diǎn)什么手腳,所以先創(chuàng)建一個(gè)代理對象給你用著先,等你動完該動的手腳,才開始真正創(chuàng)建實(shí)例。也就是說,這里我們調(diào)用create方法本來是要返回一個(gè)call對象的,但是!我們要在傳入需要的參數(shù)后才能真正創(chuàng)建call對象,也就是說在文中開始的第三步后才真正創(chuàng)建了一個(gè)call。

這里筆者就不探究動態(tài)代理如何實(shí)現(xiàn)的了(因?yàn)槲乙膊粫?.)我們繼續(xù)往下看,這動態(tài)代理內(nèi)的代碼

首先是調(diào)用了自身的loadServiceMethod方法,加載(獲得)了一個(gè)ServiceMethod類,然后通過其創(chuàng)建了一個(gè)OkHttpCall實(shí)例,最后調(diào)用了serviceMethod實(shí)例中的一個(gè)callAdapter對象中的adapt方法,最終返回了一個(gè)call。為這個(gè)adapt方法就是之前分析中的工廠方法產(chǎn)品類的方法。
查看一下loadServiceMethod的代碼

看來Retrofit內(nèi)部保存了一個(gè)ServiceMethod的map(key是從接口中反射出來的方法),沒找到的話就通過ServiceMethod的Builder構(gòu)造一個(gè)(又是一個(gè)構(gòu)造者方法),然后存進(jìn)map中。而ServiceMethod中就會創(chuàng)建callAdapter,這其中又會跳回Retrofit中,從其維護(hù)的callAdapter工廠列表中查找..挺復(fù)雜的,就不說啦,畢竟原則是設(shè)計(jì)模式分析嘛。
再看看用serviceMethod構(gòu)造的OkHttpCall

其實(shí)現(xiàn)了call接口,就是之前在分析適配器講到的call接口一樣的。里面包含了一個(gè)另一個(gè)call接口:rawCall,這個(gè)rawCall與上面的call不同,因?yàn)樗莵碜詏khttp3的。也就是這里的OkHttpCall其實(shí)就是包裝了okhttp3中的call,功能的調(diào)用都委托給他..又一個(gè)裝飾器。
總結(jié)
到這里,Retrofit中用到的設(shè)計(jì)模式我能發(fā)現(xiàn)到的,基本分析完了,里面運(yùn)用了抽象工廠,工廠方法,單例,裝飾器,適配器,動態(tài)代理。光是配置這一步就用了前六種設(shè)計(jì)模式..太可怕了..
希望你們能看懂...虛心接受指導(dǎo)和更正..