前言
最近在思考关于iOS视图架构的一些东西,于是开始纠结MVC、MVVM等架构。由于项目里原来的代码比较乱,日积月累,维护的人也换了又换,可以说到了十分臃肿难以维护的地步。所以借某个机会得以对其进行重新设计。项目里的业务逻辑比较多,也比较乱。所以必须把架构做好,以方便后期的维护。
说回视图层架构,这阵子参考了很多资料,包括MVC、MVVM等,每个人的说法都不完全一致。只有一种解释,那就是架构模式不是一层不变的,而是在不断发展的,正如项目也在发展,项目的代码也在重构。所以,这篇文章谈到的不一定是这些架构最真实的概念,而是我对它们的理解。中间的理解也可能会存在某些问题,欢迎提出来然后一起完善。
同时,这篇文章所说的架构都是针对iOS的视图架构,而对于Android和Web等,虽然原理相似,但在实际开发过程中却可能出入很大,甚至适用情况都是不同的。所以还需要其他相关人员来补充。
View层架构
说到架构当然少不了MVC,后续发展出了MVP,以及升级版的MVCS、MVVM等。这些架构其实都是想通的,但在学习的过程中总是感觉有很多无法理清的地方,包括它们最原始的概念、它们的发展、以及适用情况等。
MVC
MVC把架构划分为三个部分,分别是Model、View和Controller。可以说,三层架构是一切视图层架构的基础,其他的模式都是从三层架构发展而来的。
-
View:主要负责视图的显示
-
Model:主要负责数据的管理
-
Controller:协调View和Model
MVC是一个比较古老的模式,我们先来看看MVC的基本结构。
从上图我们可以看到这样的关系链:
-
View向Controller传递交互信息
-
Controller通知Model改变数据
-
View从Model获取数据显示到视图中,Model更新数据后通知View更新视图
事实上,这是最传统的MVC结构。在这种结构下,View和Model互相持有,甚至View和Controller以及Controller和Model的关系都是千丝万缕,非常不利于维护。所以,后来的MVC发展出了新的结构。
现在来看看新的关系链:
-
View向Controller传递交互信息
-
Controller通知Model改变数据
-
Model更新数据后通知Controller改变数据
-
Controller得知数据改变后通知View更新视图
可以看到,演变出来的新MVC去掉了View和Model之间的联系,让View只与Controller交流,而Model也只与Controller交流。而这样的结构也称之为重量级视图控制器结构,除了视图部分和数据部分,其余的都交给Controller,后面会继续讲到。
所以,现在所说的MVC,基本都是指新的视图控制器。View和Model之间的解耦有利于项目的开发,让负责不同模块的开发人员不用担心自己的改动影响到别人的代码。
MVP
MVP是从MVC发展来的,它基本与新的MVC一致,只是详细定义了MVC中各个部分的职能以及它们的交流方式,并让视图和控制器之间通过接口交流。
在MVP里,Controller的概念换成了Presenter,主要职能是用来展示界面,但其实和Controller没有太大的差别。
MVP最重要的就是在View和Presenter之间新增了一层IView的接口,View和Presenter通过IView来交流。之前说到新的MVC中View和Model进行了解耦,而在MVP中,View和Presenter进一步解耦。让视图只做视图的功能,而控制器只做控制器的功能,两者通过接口交流,互不影响。
可见,MVP和MVC还是十分相似的。
MVCS
MVCS是从MVC发展而来的,MVCS也不是什么高深莫测的架构,只是把Model中的数据存储部分Store分离开来。Model负责数据的管理,而Store负责数据的存储。
MVVM
MVVM,虽然它的名称里没有C,但并不表示它就没有Controller。相反,由于MVVM里的Controller和View联系紧密,已经融为一体了。所以,可以把MVVM理解为MVCVM,或者把MVVM里的V理解为VC,即View和Controller。
MVVM里的VM指的是ViewModel,它把MVC中原本属于Controller的业务逻辑部分抽离出来形成了ViewModel。这样,Controller里只剩下和View交互相关的部分,而业务逻辑这种与视图显示、视图交互无关的部分则独立为ViewModel。
所以,Controller和View结合到一起,和ViewModel互相交流,而ViewModel再和Model交流。其实这样的方式和MVC是十分相似的,同样是视图和业务逻辑交流,业务逻辑则和数据交流。
MVVM还有一个重要的概念,就是数据绑定。数据绑定使得View和ViewModel的交流更加方便,相当于把视图的数据绑定到业务逻辑中,任何一方改变都会影响到另一方的改变。当然,数据绑定并不是必须的,使用通知、观察者模式、代理等都可以实现MVVM。
胖与廋
胖与瘦,指的是一个层是否臃肿,是否包含了很多逻辑、很多代码。而比较有争议的就是胖Model和瘦Model,因为胖Model意味着瘦Controller,而瘦Model则意味着胖Controller。
瘦Model,是MVC的一个特征。因为Model只负责了数据管理部分,如果Model大了,可以分出Store,但再怎么分,还是数据管理。此时的Controller,既负责了View层的交互控制,也包含了业务逻辑(业务逻辑中分为强业务逻辑和弱业务逻辑)。如此,Controller自然要负责很多东西。
而胖Model,则要求把Controller的弱业务逻辑移到Model中,为Controller减负。这样,Controller则负责和View的交互以及强业务逻辑。
其实,胖与瘦的Model,只是决定了弱业务逻辑的位置。瘦Model的弱业务逻辑放在Controller中,而胖Model的弱业务逻辑放在Model中。
那我们应该提倡哪一种呢?
事实上,弱业务逻辑应该尽可能的独立出去,并使用胖Model方式。原因有三。
-
弱业务逻辑应该从Controller分离出去,因为等到Controller臃肿的时候再想分离的话会比较困难。
-
弱业务逻辑应该放到Model中,因为Model封装了数据的处理,弱业务逻辑属于数据处理部分。
-
弱业务逻辑包含到Model并保持独立,便于Model的维护,也便于弱业务逻辑的测试。
所以,当一个逻辑你不知道写到哪个层的时候,写到Model层,这样后期想要重构的话也会十分方便。
架构的关键
架构的关键只有一个字,那就是“拆”。
其实架构都是在不断发展的,每个架构都是在之前架构的基础上发展而来,从上面的架构来看,架构之间也是相似的,必然包含View、Model和Controller。
A拆为MV
A表示一个应用。一开始,如果应用很小,你可能会想把所有代码写到一起。而项目大了,为了便于管理,自然要把它拆为Model和View,因为你知道Model是不变,而View总是会改变。
V拆为VC -> MVC
而当View多了之后,或者一个View里面有太多交互逻辑的时候,你就会考虑把View里的Controller部分拆开来。因为View有时候是不变的,但交互方式经常会发生改变。这样的拆法就是MVC的雏形。至于各个层之间的交流方式如何,则任你定义。
M拆为MS -> MVCS
Model层也会变大,那自然也得把不变的部分拆了,于是把数据存储部分拆为Store。这样就成了MVCS。
C拆为CVM -> MVVM
Controller变大之后,把它的业务逻辑和交互逻辑拆开,让Controller只负责交互,而业务逻辑封装到ViewModel里。也就成了MVVM。
M拆为VM和S -> MVVMS
如果MVVM里觉得M太大了,自然可以和MVCS一样把Model拆出Store来,起名为MVVMS。
小结
所以,只要你需要,你可以把MVC的任意一层拆了,拆出一层或两层都可以,只要觉得有需要就可以拆。所以,其实并没有所谓的太多架构,其他架构都是人们为了拆MVC而起的新名字。
原则
解耦
解耦是必须的,任何一个层之间耦合太大,都不便于维护。试想一下,Controller里面对视图及子视图任意调用,同时任意调用Model的属性方法,而View层和Model层互相持有,有朝一日想要修改视图,那可是吐血的心都有了。
面向接口
面向接口是重要的,但是是可选的。面向接口编程有利于开发和维护,但必须接口明确。结构良好的项目,针对经常发生变化的模块,可以建立接口,这样就避免了每次改变都需要修改代码。但如果项目不大,项目不规范的话,也不太好定义接口。而有时候某些模块不怎么变化,定义接口也会增加很多工作。
所以,如果要定义接口,就应该采取一套规范,并尽早定义。
单一原则
把同一类逻辑放到同一个模块,不同逻辑不要放到同一个模块。如果是处理图片的就写到图片的模块里,如果是处理字符串的就写到字符串里,而不要因为它们都是解决相近的问题或者因为它们代码不多就写到一个文件里,这样后期会增加很多维护成本。
总结
这一次的学习也让我渐渐地理清了代码的架构,为后面的代码架构做好基础。
对于任何一个问题,最好都必须进行系统性的学习。但是现在网上可能没有太多系统性的资料,书本的知识也很容易过时。这就需要我们快速地从网上寻找大量的资料,分门别类地学习,并尽快总结。总结了,才能从中获取真正的知识。