關於MVC的爭論已經有不少,對此個人觀點是:對於iOS開發中的絕大部分場景來講,MVC自己是沒有問題的,你認爲的MVC的問題,必定是你本身理解的問題(資深架構師請自動忽略本文).html
行文過程當中查閱了互聯網上的大量文檔,其中水平參差不齊(最多見的就是MVC改個名就當MVVM的),固然也有許多很是有價值的參考資料,在文末會逐一列舉,以供參考.ios
根據官網上的描述, Cocoa中的MVC是這樣的:程序員
Model Objects Encapsulate Data and Basic Behaviors後端
View Objects Present Information to the User安全
Controller Objects Tie the Model to the View服務器
經過搜索引擎,發現其實MVP其實兩種的:網絡
Passive View數據結構
Supervising Controller架構
網上絕對部分談論MVP的文章談論的其實都是Passive View,這裏放上一張Passive View的示意圖:mvc
乍一看上去是否是和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視爲V層,即上面MVP示意圖中的Passive View,那麼UIViewController將只負責View佈局相關邏輯,不涉及任何與Model層的交互!!
不涉及任何與Model層的交互!!
不涉及任何與Model層的交互!!
全部的業務邏輯交互經過UIViewController持有的Presenter與Model間的調用來完成.
那若是把UIViewController視爲C層,從MVC設計理念上來講,C層不會負責具體View的佈局及展現邏輯,可是因爲iOS中UIViewController的特殊設計,致使不少開發者直接就在UIViewController包含了不少具體佈局相關的代碼,更可怕的狀況是不止是View的初始化,包括網絡請求及具體業務處理也被包含到UIViewController中,這也許就是有人戲稱MVC爲:MassiveViewController的緣由.
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層徹底與業務邏輯徹底隔離開.
回到項目重構的問題上來,我認爲項目重構首先要想清楚的問題:
項目層級如何劃分?
大的業務場景有哪些?
將UIViewController歸類爲View仍是Controller?
誰來負責網絡請求,Model仍是Controller?
從Model中取得數據後Controller怎麼把數據傳遞給View去展現?按照View層級逐級傳遞?是否須要使用ViewModel?
Model的生命週期怎麼控制?(全局Model和私有Model的劃分)
View層級結構愈來愈深時,怎麼傳遞用戶的交互操做?(毫無疑問NSNotification)
本次重構中仍是將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的示意圖:
看上去是否是很像MVP?只是多了View和ViewModel的雙向綁定,這裏強調一點,RAC不必定登錄MVVM,MVVM也不必定要使用RAC.