【iOS开发工程化——开发中】:三层架构,MVC、MVVM

目录
一、三层架构
二、视图层架构
  1、MVC
  2、MVVM


一、架构分层


上一篇我们说到组件化开发是一个App最大一层的架构,那具体到了一个一个的业务组件里,比组件化开发小一点的架构就是三层架构。所谓三层架构就是指把项目中所有的类划分到不同的层,目的是使项目逻辑更加清晰,提升开发和维护的效率,我们一般会把项目分为三层:数据层、业务层、视图层。

  • 数据层一般就是对网络三方库的封装和对本地数据三方库的封装,调用数据层你就可以获取到rawData;
  • 业务层内部在调用数据层的API,业务层主要负责的是决定数据的来源是从网络请求还是本地数据,然后等着数据层给它返回rawData,把rawData处理成视图层能直接使用的Model、ModelArray等模型数据回调给视图层;
  • 视图层内部在调用业务层的API,视图层主要负责的是告诉业务层它想要什么数据(即想做什么业务)并传递一些必要的参数给业务层,然后等着业务层返回数据后直接拿着用就可以了。

注意:严格来讲,三层架构并不真正就比组件化开发小,它们俩只是从不同的角度去做架构而已,往往是相互融合的,比如组件化开发里网络库就是三层架构里数据层的内容,UI库就是三层架构里视图层里的内容等,这里之所以说“三层架构是比组件化开发小一点的架构,主要是针对业务组件来说的”,当然甚至我们在不使用组件化开发时项目里依然可以使用三层架构。


二、视图层架构


比三层架构再小一点的架构就是视图层架构,我们常说的MVC、MVVM都是˛视图层的架构。

1、MVC

苹果版MVC

MVC,即Model-View-Controller,它们的关系如下:

Model的职责:

  • Model只负责封装数据,不做任何其它操作。

View的职责:

  • View负责显示数据,那怎么显示数据呢?View需要把供外界使用的子组件给暴露出去;
  • View负责响应与业务有关的事件并交给Controller去处理,怎么交给Controller呢?通过代理、block、通知等;
  • View负责响应与业务无关的事件,如某些事件会引发动画效果等,这应该尽量在View内部完成。

Controller的职责:

  • Controller负责持有View,创建View,并把View添加到窗口上显示;
  • Controller负责加载Model,加载完后就持有Model并显示在相应的View上;
  • Controller负责监听View与业务有关的事件,并通过与Model的合作,来完成相应的业务;
  • Controller负责监听Model的变化,并通过与View的合作,来完成相应的业务。

苹果版MVC的优势就在于:

  • Model和View之间没有一点儿耦合,它们可以独立使用,具有高度复用性。

缺点也很明显:

  • Controller的代码过于臃肿,这主要体现在View的很多子组件暴露在外面,Model给View赋值的那个地方。
变种版MVC

变种版MVC是针对苹果版MVC的缺点进行的,变种的核心就在于:View持有了Model,把数据显示的逻辑放在了View内部来处理,而不再是通过View暴露子组件的方式来显示数据了。

变种MVC的优势就在于:

  • 在一定程度上为Controller瘦了身;
  • 将View内部的实现细节封装起来了,外界不需要知道View内部的实现细节,只需要给它一个Model就可以了。

缺点也很明显:

  • View明显耦合于Model,这个View无法再独立使用,必须绑定某个特定的Model一起使用。(不过我觉得这是可以接受的,毕竟我们既然选择了这种架构,就表明这种View专门就是用来绑定某个特定的Model显示的,而如果我们需要让一个View能显示各种Model,那可能就会选择苹果版MVC,也就是说实际开发中可能苹果版MVC和变种版MVC都有)

2、MVVM

但无论是苹果版MVC还是变种版MVC,它们都有一个致命的缺陷,那就是随着业务的增加,C会变得越来越臃肿,几千行的代码维护起来简直就是噩梦,这主要是因为C干了太多的事,比如获取数据、处理数据、存储数据等。

那有没有办法解决这种情况呢?经过多年的实践证明,MVVM是解决这种情况的有效办法。

MVVM,即Model-View-ViewModel,它们的关系如下:

MVVM就是把MVC里Controller里获取数据、处理数据、存储数据的操作,还有其它一堆业务逻辑的代码都放到了ViewModel里,从而达到为Controller瘦身的效果。

  • 所谓获取数据的操作是指调用业务层的API获取数据;
  • 所谓处理数据的操作是指ViewModel会针对Model一对一地创建外界真正使用到的属性、这些属性属性通常搞成getter方法就可以了、对Model的原始属性进行处理,以便外界能够直接使用;
  • 所谓存储数据的操作是指ViewModel会把获取并处理后的数据保存在自己内部供外界使用,而不是暴露给外界让Controller去持有;
  • 其它一堆业务逻辑的代码。

注意一个ViewModel里不是非要把这几件职责凑齐才配叫ViewModel,有其中的一部分也可以。

通常情况下ViewModel的创建主要有两个触发点:

1、为每个Model创建对应的ViewModel:只要接口返回的数据不是非常不符合主流的设计规范,那么如果接口返回的是字典,那这个字典绝对可以对应为Model,那么只要有一个Model,我们就得有与之对应的ViewModel来处理Model的数据————Model可能会有很多属性,但View层只会展示部分属性,而且有的属性得处理一下才能供View层直接使用,所以这个ViewModel就会针对Model一对一地创建外界真正使用到的属性、并对Model的原始属性进行处理以便外界能够直接使用、这些属性属性通常搞成getter方法就可以了(Swift、Dart直接搞getter方法就可以,OC得创建property然后再提供getter方法),此时这个ViewModel已经体现了ViewModel的模型性;这种情况下的ViewModel是适合添加获取数据、存储数据、处理其它业务逻辑的代码的,尽管去添,这样这个ViewModel就是具备了模型性和逻辑性的完整ViewModel;

2、ViewModel太小了不适合管更大的事、于是创建一个更大的ViewModel:但如果接口返回的是数组,一般都是数组内部的小字典才对应为一个Model,那当我们为这个Model创建对应的ViewModel之后,就意味着数组内的小字典才对应ViewModel内部的那个model属性,而我们又必然需要把接收到的model数组转换成viewModel数组,但是这个viewModel数组是不宜直接存储在ViewModel内部的,因为这个viewModel数组里存储的就是一个一个的ViewModel本身,而这个ViewModel的定位其实是对单个Model的一对一包装、然后顶多是处理一下跟单个Model有关的业务逻辑,那这时如果允许它内部出现一个存储了一堆它自己——即ViewModel——的数组,那就意味着这个ViewModel除了需要处理跟它一对一的那个model的业务逻辑,还需要处理一堆跟它并没有多大关系的model的业务逻辑,这显然违反了初衷,所以此时这个ViewModel最好不要多管闲事,只体现ViewModel的模型性就足够了;此时我们就得再创建一个更大一层的ViewModel来处理业务逻辑了————获取数据、存储数据(这个大ViewModel一般会持有第一步的小ViewModel所构成的数组)、处理其它业务逻辑的代码,其实这个触发点也是很好找的,一般就是第1个触发点创建出来的ViewModel只是一个Item对应的视图模型,不适合去管更大的事,而这个更大一层的ViewModel则是统筹全部Item的视图模型,而这个更大的ViewModel提现的更多的就是ViewModel的逻辑性。

综上,如果接口返回的是字典,我们创建一个ViewModel就够了,这个ViewModel既能体现模型性也能体现逻辑性(学生答题项目的接口设计是非常好的,基本就是这种情况,代码逻辑非常清晰);而如果接口返回的是数组,我们就得创建两个有关联的ViewModel了,小ViewModel专门处理数组里小字典的数据,着重体现模型性,大ViewModel专门处理获取数据、存储数据、处理其它业务逻辑的代码,着重体现逻辑性(智能白板项目的接口设计有的不是那么好,可能会出现这种情况,因为需要分两个ViewModel);当然如果随着业务的增加“一个ViewModel”或者“大ViewModel”里的代码越来越复杂越来越庞大,那我们也可以考虑按业务模块创建单独业务模块的ViewModel,然后让“一个ViewModel”或者“大ViewModel”去持有这些单独业务模块的ViewModel来做整体的业务调度。(智能白板里演讲模式模块、跟随模式模块、分布式数据存储模块就开始采用这种方式了)。

其实做完上面的操作就已经是MVVM了,但是业界经常会在上面MVVM的基础上再加个响应式编程,响应式编程可以为MVVM提供更加简单优雅的双向绑定,主要是指数据驱动UI,常用的响应式编程框架有RAC和RxSwift,可参看【RxSwift】RxSwift的理论知识【RxSwift】RxSwift在MVVM方面的实际应用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容