今天來介紹一個適合構建大型APP或比較大的模塊的架構-VIPER;
為什么用VIPER?
略
什么是VIPER?
伴隨著業務的增加、功能模塊的增漲我們會發現,代碼的結構越來越不清晰、測試也越來越難,陷入了一個惡性循環;
為了使得代碼便于維護、結構更清晰、便于測試,這時候我們就需要一種好的架構方式來組織我們的APP代碼,這時候VIPER就應運而生;
VIPER這個概念來自于Clean Architecture,VIPER是View, Interactor, Presenter, Entity,Routing,這幾個單詞的縮寫;在Clean Architecture中將整個APP或者說模塊用專門的不同的層次來分隔,這使得各個層之間減輕依賴,每個層次負責單一的功能,也符合功能單一的設計原則,也更加的便于測試;
拆分VIPER
View
- 就是UIViewController/View
- 展示Presenter傳遞過來的數據
- 收集用戶輸入、或者事件等交互信息回調給Presenter
View是被動的,它等待Presenter傳遞內容用于展示,它從不主動去向Presenter請求數據;
Presenter
- 負責協調Interactor和View之間的邏輯
- 將Interactor傳遞過來的Model轉轉換為ViewModel
- 傳遞ViewModel給View
- 處理從View中傳遞過來的交互事件
- 在需要的時候,通過調用Router導航到新頁面
Presenter是整個VIPER架構的核心,在Presenter中包含驅動UI的來邏輯,他知道什么時候展示什么界面;它負責收集從View中傳遞過來的數據或者事件,從而去驅動Interactor請求數據、或更新UI;當接收到Interactor傳遞過來的數據,在將數據轉換成ViewModel后,將數據傳遞給View;
Interactor
- 包含業務邏輯、規則滿足具體的業務需求
- 負責將Entity轉換為Model
- 知道在數據傳遞給Presenter前需要執行哪些動作,比如存儲、刪除數據等
- 不關心具體的數據來源,通過其他的Server API來獲得數據
Interactor包含Entity關聯到具體業務的邏輯,比如只有符合某種條件的Entity才會被傳遞到Presenter,在其內部不應該和任何的UI產生依賴關系;因為Interactor負責的是純業務邏輯,所以他應該很容易進行測試或者通過TDD進行開發;
Entity
- 業務數據模型
- 一般直接是沒做過轉換處理的接口返回數據
- 直接也僅和Interactor產生關聯
Router
- 整個模塊的入口及出口
- 一定包含一個方法負責創建當前模塊,一般是返回一個關聯整個模塊的UIViewController;
- 負責頁面的導航,從當前模塊到其他模塊,比如push、present到新的頁面
在VIPER中,頁面的切換響應是由Presenter和Router一起承擔的,由于Presenter負責收集用戶的輸入,所以他知道什么時候需要切換到哪個新的頁面,而Router內包含有具體的切換方法,知道怎樣去切換;
我們也可以將Router中創建整個模塊的功能分離出來,創建一個單獨的Buider模塊,這就是VIPER-B;
更加詳細的劃分關于VIPER-B,見下圖(圖片的來源再這里):
其他模塊介紹
Buider
- 整個模塊的入口
- 包含一個創建方法負責創建當前模塊,一般是返回一個關聯整個模塊的UIViewController;
Data Manager
- 是對負責網絡請求的Server,本地存儲Server的封裝
- 不負責具體的網絡請求或者存儲
- 可以被多個Interactor訂閱
Model
- 是對entity的封裝
- 不是能夠直接展示的數據,記得ViewModel的轉換時Presenter的工作
ViewModel
- 由Presenter負責生成
- 包含有UI需要顯示的內容
viper中各個模塊的整合
我們知道了各個層次的職責,將所有的層次組合到一起才是一個完整的架構;怎樣將這些不同的層整合到一起也是一個問題,根據簡單復雜有兩個辦法;
方法1:
- 像上圖一樣,view直接持有present,同時view成為present的代理,這樣能夠使得他們之間實現雙向交互
- router直接被present持有
- interactor直接被presenter持有,interactor回調或者通知presenter數據進行更新
方法二:
同樣通過協議代替上面的持有,將上面辦法中的持有完全由代理代替,即Presentor、View各自成為各自協議的代理;但是需要注意避免循環引用及完全弱引用,其中一方的代理需要用強引用,一方代理需要弱引用;