关于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.