前言
最近學校的設計模式課程設計要求我們選一個框架進行設計模式的分析,結果老師給的題目都是跟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中用到的設計模式我能發現到的,基本分析完了,里面運用了抽象工廠,工廠方法,單例,裝飾器,適配器,動態代理。光是配置這一步就用了前六種設計模式..太可怕了..
希望你們能看懂...虛心接受指導和更正..