你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> iOS 基於 MVC 的項目重構總結

iOS 基於 MVC 的項目重構總結

編輯:IOS開發基礎

1.jpeg

關於MVC的爭論

關於MVC的爭論已經有很多,對此我的觀點是:對於iOS開發中的絕大部分場景來說,MVC本身是沒有問題的,你認為的MVC的問題,一定是你自己理解的問題(資深架構師請自動忽略本文).

行文過程中查閱了互聯網上的大量文檔,其中水平良莠不齊(最常見的就是MVC改個名就當MVVM的),當然也有許多非常有價值的參考資料,在文末會逐一列舉,以供參考.

iOS中的MVC和MVP

Cocoa版本的MVC

根據官網上的描述, Cocoa中的MVC是這樣的:

blob.png

  • Model Objects Encapsulate Data and Basic Behaviors

  • View Objects Present Information to the User

  • Controller Objects Tie the Model to the View

C和P的差別

通過搜索引擎,發現其實MVP其實兩種的:

  1. Passive View

  2. Supervising Controller

網上絕對部分談論MVP的文章談論的其實都是Passive View,這裡放上一張Passive View的示意圖:

blob.png

乍一看上去是不是和MVC一樣?我也一直一頭霧水,直到在stackoverflow看到這個答案:


MVP: the view is in charge. The view, in most cases, creates its presenter. The presenter will interact with the model and manipulate the view through an interface. The view will sometimes interact with the presenter, usually through some interface. This comes down to implementation; do you want the view to call methods on the presenter or do you want the view to have events the presenter listens to? It boils down to this: The view knows about the presenter. The view delegates to the presenter.


MVC: the controller is in charge. The controller is created or accessed based on some event/request. The controller then creates the appropriate view and interacts with the model to further configure the view. It boils down to: the controller creates and manages the view; the view is slave to the controller. The view does not know about the controller.


簡而言之,MVP是View驅動的,View層持有一個對應Presenter的引用,View上的交互事件首先會調用Presenter提供的接口,然後Presenter調用Model提供的方法取得數據,最後Presenter將取得的數據傳遞到View上展示.

MVC則是由Controller驅動的,Controller持有View,並響應View上的交互事件,根據交互調用不同的Model方法取得反饋數據,再將數據傳遞給View展示.

我的理解是,MVP是用戶視角:所見即View.MVC則是程序員視角:I control everyone.

理解MVC和MVP的差別困惑的地方在於,UIViewController到底是屬於C(P)層還是V層呢?下面將分別具體分析一下這兩種觀點.

觀點一:UIViewController的歸屬--->View

如果把UIViewController視為V層,即上面MVP示意圖中的Passive View,那麼UIViewController將只負責View布局相關邏輯,不涉及任何與Model層的交互!!

不涉及任何與Model層的交互!!

不涉及任何與Model層的交互!!

所有的業務邏輯交互通過UIViewController持有的Presenter與Model間的調用來完成.

觀點二:UIViewController的歸屬--->Controller

那如果把UIViewController視為C層,從MVC設計理念上來說,C層不會負責具體View的布局及展示邏輯,但是由於iOS中UIViewController的特殊設計,導致很多開發者直接就在UIViewController包含了很多具體布局相關的代碼,更可怕的情況是不止是View的初始化,包括網絡請求及具體業務處理也被包含到UIViewController中,這也許就是有人戲稱MVC為:MassiveViewController的原因.

Model,DataInfo以及對胖瘦Model的爭議

Model和DataInfo的差異

MVC架構思想中更傾向於Model是一個Layer,而不是一個Object(Java或Android中的bean).

所以這裡的DataInfo我將其定義為一個DTO(Data Transfer Objec),更簡單的來說,就是一個數據結構,跟我們在學校學習C語言時寫的一個student結構體基本是一個意思.

我的理解是,Model是用來處理業務邏輯的,可視為傳統開發三層架構中的BLL(Bussiness Logic Layer)和DAL(Data Access Layer)的合體,負責所有的具體業務.比如,對一個包含安全認證的App,你可能需要一個AuthModel來負責所有的認證邏輯以及認證信息的本地持久化.這樣,Controller中只需要調用AuthModel提供的接口便能完成相應安全認證功能,職責分明.

Model中的一些方法的產出DTO,用來更新View.例如UserModel會去查詢用戶信息,然後將服務器返回的用戶信息轉換成一個本地的UserInfo,將這個UserInfo傳遞給View進行展示.

因此業界關於胖瘦Model的爭議,我更多的理解是對於DataInfo是否需要提供涉及到業務的擴展功能的爭議.

業務場景舉例

舉例一個業務場景,一個用戶信息View上需要展示用戶性別,一般來說服務器只會在返回的用戶信息中包含一個sex字段(這裡用0代表女性,1代表男性),需要使用的時候可能需要使用if語句進行判斷然後輸出不同的性別文字或圖片.

從個人習慣上來說,很多開發者會將服務器返回的用戶信息轉換成一個本地的UserInfo DTO,然後將這個DTO傳遞給要對應需要展示的View,然後在View中進行輸出判斷.

當然,開發者可能會使用現在很流行的一些字典轉模型框架(YYModel,MJextension等),也可以使用這些框架提供的配置接口在轉換時就實現這種輸出邏輯的轉換,或者直接在UerInfo sex屬性的getter方法中進行轉換後輸出.

不管怎樣,只要在DataInfo這個層級上做了類似的這些轉換,那麼業務邏輯就已經侵入了DTO的定義.

一種解決思路

但是,這種場景幾乎又是不可避免的,如何解決呢?被誤解的 MVC 和被神化的 MVVM提出了一種借鑒MVVM的解決思路,具體做法就是將 ViewController 給 View 傳遞數據這個過程,抽象成構造 ViewModel 的過程.這樣抽象之後,View 只接受 ViewModel,而 Controller 只需要傳遞 ViewModel 這麼一行代碼。而另外構造 ViewModel 的過程,我們就可以移動到另外的類中了.

在我看來,這樣ViewModel層其實只是把上文中對DataInfo的擴展單獨提煉了出來.這樣就將View層完全與業務邏輯完全隔離開.

本次基於MVC的項目重構步驟

思考要解決的問題

回到項目重構的問題上來,我認為項目重構首先要想清楚的問題:

  1. 項目層級如何劃分?

  2. 大的業務場景有哪些?

  3. 將UIViewController歸類為View還是Controller?

  4. 誰來負責網絡請求,Model還是Controller?

  5. 從Model中取得數據後Controller怎麼把數據傳遞給View去展示?按照View層級逐級傳遞?是否需要使用ViewModel?

  6. Model的生命周期怎麼控制?(全局Model和私有Model的劃分)

  7. View層級結構越來越深時,怎麼傳遞用戶的交互操作?(毫無疑問NSNotification)

UIViewController的劃分

本次重構中還是將UIViewController歸類為C層,但是為了將UIViewController徹底和UIView分離開,命名上我們直接使用XXXController,我們對每一個XXXController設計了一個對應的名為的XXXContainerView的UIView對象,所有的view布局都會在這個XXXContainerView中完成.

項目目錄結構

重構後的項目目錄結構如下:

一級目錄 子目錄 目錄說明 Macro 
存放開發過程中所需的一些宏定義 Category 
存放不涉及業務,用來輔助開發的分類 Tools 不同的業務工具類 存放涉及輕量級業務的處理類,比如根據不同業務格式化輸出不同的字符串 Views 不同的業務模塊頁面名 存放不同業務模塊頁面下的V Controllers 不同的業務模塊頁面名 存放不同業務模塊頁面下的C ViewModels 不同數據模塊名 數據翻譯層,將DataInfo數據翻譯為View可直接展示的數據,但本次重構中由於時間因素不強制使用 Model PublicModel 公用的全局Model,比如用戶信息UserModel 
MoudleModel 單獨某個模塊使用的私有Model,只負責私有業務 Services 不同的Service 提供底層服務,例如HttpService,SecurityService 

業務場景劃分

由於之前趕進度開發,沒有做具體的功能拆分.本次重構之前使用了StartUML繪制了主要場景下的UML圖,包括類圖,時序圖,流程圖.

強烈推薦新項目正式編碼之前就完成這一步,並由前後端技術負責人主持進行UML評審.所有涉及到的業務Model的property以及public方法,所有DataInfo類的屬性,甚至所有的Controller都會在繪制UML的過程中產出.當然,要想繪制所有場景下的UML圖可能耗時比較久,一般來說只要繪制出主要交互場景即可.

網絡請求

在手機端基本上所有的網絡請求都是跟業務掛鉤的,顯然放在Model裡更合適.至於請求完成後的回調就看個人習慣了,delegate或者block都是可行的.

後續

由於時間原因,並沒有寫出具體的示例代碼,之後會針對一個示例場景,寫出我理解中的各個MV(X)模式的參考代碼,期待成文後與同行探討.

MVVM擴展

放一張MVVM的示意圖:

blob.png

看上去是不是很像MVP?只是多了View和ViewModel的雙向綁定,這裡強調一點,RAC不一定登陸MVVM,MVVM也不一定要使用RAC.

參考資料

  • 被誤解的 MVC 和被神化的 MVVM

  • objc中國-MVVM 介紹

  • iOS應用架構談系列

  • iOS 架構模式--解密 MVC,MVP,MVVM以及VIPER架構

  • Model-View-Controller


  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved