序言
学如逆水行舟,不进则退。共勉!!!如果你想成为一名机构师,就沉下心来看这篇文章。不说你看完这篇文章,会怎样怎样,但是至少不会让你看这篇文章等于在浪费生命。废话不多说,直接上干货。
MVC
如果你想成为匿名架构师,首先要知道MVC,经典就是经典,没有之一,因为称为经典也是有原因的。iOS中MVC架构,看懂斯坦福大学白胡子老头教授这张图基本上就可以了。
简单的来理解,就是Controller对象拥有View和Model对象,两者通过Controller进行沟通。对于单个页面,三个类就搞定了,感觉很简单。
⽹络连接应该放在哪⾥?Model中吗?感觉很有道理?实际上,很多的⽹络连接的发起和接收后 的处理都放在了Controller中,因为⽅便嘛。Model⼀般只有属性定义,没有实现。
View应该是独⽴⼀块了吧?实际上呢,View⼤多都放在了Controller中,有个loadView函数,很⽅ 便啊。有⼏个⼈会单独写个类来作为view?
本来,xib和Storyboard是很好的分离view的⽅式。但是,由于“不合适多⼈合作,版本管理”,⾮ 要代码写界⾯,还振振有词:“性能⾼,对培养新⼈有好处”。“谎⾔说100次都能成真话”,何况这些 理由听上去还那么有理。
像“检查⽤户名是否合法,检查密码对不对”应该放在哪⾥呢?有⼏个⼈会像斯坦福⼤学⽩胡⼦⽼头 那样新起⼀个类来写?基本上都是Controller中搞定。
BaseController,BaseView,BaseModel⼀定⻅过不少吧?有的还有好⼏层呢 公共View,各种名字带common或者类似的类常⻅吧?⾥⾯⽹络连接,数据库,逻辑等等往往⽐ View本身很多,俨然⼀个⼩模块了,功能⽐Controller都强⼤了。这还是view吗?
本来MVC理论上是最简单的架构,但是实际结果呢,变成了最难懂的架构。Controller成了上帝 类,什么都⼲。“只知道那⼀坨东⻄有⽤,但看不出那是简单的MVC”。
MVC也被称之为 Massive View Controller(重量级视图控制器)。其实这不是MVC的错,只是没 有程序员承认⾃⼰懒惰,编程习惯不好罢了。如果能够像斯坦福⼤学⽩胡⼦⽼爷爷那样好的编程习 惯,那么⼤部分的iOS程序都能有清晰的MVC架构。
MVVM
MVVM来⾃MVC,⼀张经典的图就是下⾯这张,在好多⽂章中看到过。
“稍微考虑⼀下,虽然 View 和 View Controller 是技术上不同的组件,但它们⼏乎总是⼿牵⼿在⼀ 起,成对的。你什么时候看到⼀个 View 能够与不同 View Controller 配对?或者反过来?所以,为 什么不正规化它们的连接呢?” ------ 这段话当时给我的印象很深刻,这个观点到现在我都认可。 Controller代表了⼀个场景(Scene)的⽣命周期,是⼀个调度者。什么都是,因为什么都离不开 它。⼜好像什么都不是,因为它代表不了任何具体的东⻄。让它和View合在⼀起,作为⼴义的view 就有了具体的意义,并限制了它⽆所不能的印象。这点值得肯定。
“显示逻辑(presentation logic)”可以从Controller中移到ViewModel中,从⽽给Controller减负。 这个观点我也是⽀持的。并且我以此认为ViewModel就是⽤来做“显示逻辑”的,⼀个⻚⾯⼀个,随⻚⾯⽽变化。在Swift中,我⽤结构体来做ViewModel。
关于绑定机制,⽂章中推荐ReactiveCocoa。我去⼤致看了⼀下,主要是将KVO,Block, notifification,delegate等各种通讯机制统⼀为RACSignal,将界⾯和数据进⾏双向绑定,功能确实强 ⼤。但是这个⻛格和普通iOS的开发习惯差距⽐较⼤,⼀下⼦很难变过来。⽂章中也说只是推荐,不 强求,所以我也就⼀直没有采⽤。被误解的MVC和被神化的MVVM
⾄于绑定机制,在Swift中可以使⽤属性观察者。ViewModel⼀般作为Controller的⼀个属性,对它 进⾏观察,⼀旦变化,就⽤ViewModel新的值设置界⾯元素,感觉挺好⽤的。还有⼀些相隔很远或 者⼀对多的变化,⼀般可以采⽤NSNotifification来达到⽬的。
从⽹络取数据,业务逻辑(相对于显示逻辑),应该放在那⾥呢?⽂章中没有说,看意思是保留 在Controller中。还有的观点认为应该放在ViewModel中,这当然有道理,⽽且这是主流的理解。但 是这样会让ViewModel变成另外⼀个上帝类。
我理解的MVVM
这是本⼈的理解,仅仅⼀家之⾔。主流的观点没有Logic那个类,从图中删除基本上就是了。 ViewModel将是替代Controller的⼀个上帝类。
Controller主要作为调度者,居于中⼼位置。客串部分View相关功能:⽐如动画⾥⾯关于view的位 置改变,这些代码是要放在Controller⾥⾯的。这也符合Controller+View实现view功能的概念。 ViewModel专⻔做“显示逻辑”,并且⽤属性观察者做绑定,必要的时候⽤Notifification。正向的绑定 ⽐如“action-target”响应就保留在Controller中,具体事情交个其他类做就可以了。
在Swift中,ViewModel和Model推荐⽤Struct;Logic倾向于⽤class。从⼀个简单直观的概念来 说,ViewModel需要保持轻量级,跟随⻚⾯⾛,随时准备修改。Model也是轻量级,跟随后台API定 义⾛,只是个数据结构,随时准备修改。⽽Logic就显得⽐较⼤,考虑稳定,考虑复⽤。
增加Logic类,负责业务逻辑,⽐如从⽹络取数据,修改数据库,检查⽤户名合法性,具体的响应逻辑,监听后的具体处理等等
- 猿题库 iOS 客户端架构设计
⽂章中的DataController相当于这⾥的Logic,重点是Controller减负,尽量起调度者职能,具体⼯作都放到Logic中处理。Logic考虑复⽤,可以对应单个⻚⾯,也可以多个⻚⾯共⽤。按照业务逻辑的思路去划分模块。划分标准可以和⻚⾯分类标准不⼀样。对于复杂⻚⾯,View和ViewModel可以多个,按照组件的模式去考虑。对于表格,ViewModel对应的是表格的cell,dataSource数组中放ViewModel的序列。表格的delegate和dataSource,⽬前来看,放在Controller中是最⽅便的。当然,为了给Controller减负,再新增⼀个类TableDelegate也是很不错的⽅法。如果表格包含在⼀个组件中,⽤容器view做delegate和dataSource是⼀个不错的选择。主要想法就是想⽅设法“架空”Controller,让它只做⼀个调度者,管理⻚⾯的⽣命周期就可以了。实在⾮它不可的时候,才让它做具体的事情。不引⼊ReactiveCocoa等庞⼤的第三⽅库。这⾥有⼀篇⽂章不错,值得学习⼀下。
ReactiveCocoa 和 MVVM ⼊⻔VIPER
这是⽐MVVM分类更细的⼀种模式。
经典图形
-
Present:相当于ViewModel,叫展示器
-
Interactor:交互器,侧重于业务逻辑;从⽹络取数据,数据库等功能都在这⾥。
-
Entity:就是Model,仅仅是数据定义
-
WireFrame:就是Router,是⻚⾯跳转
值得借鉴的地⽅
将⻚⾯跳转独⽴出来,做成公共模块
将业务逻辑独⽴出来,做成公共模块
如果只是对于单个⻚⾯,分这么多类,感觉有点啰嗦了。不够对于多⻚⾯的模块来说,还是有借
鉴意义的
其他架构
⼀些实际在⽤,但是没有通⽤缩写名称的架构
分层模式
分层架构.png
将服务service的概念引⼊客户端,作为⼀个中间层,进⾏隔离
业务逻辑作为服务模块,通过服务的⽅式进⾏访问
数据访问跟业务逻辑,表现层分开,是跟后台的接⼝层 表现层只关注UI,相当于VIPER中的V和P;或者是MVVM中的ViewModel(仅显示逻辑)和
View,但是没有双向绑定;
平台模式
当前的APP,⼤多数是Native和H5的混合,将两者的接⼝代码统⼀成通⽤模块是⽐较好的做法 插件化也是⼀个越来越普遍的趋势,⽐如分享,第三⽅登录,⽀付等等,都由第三⽅以插件的形 式提供。对这些插件集中管理也是好的做法
APP随着公司发展壮⼤,分出不同的事业部,在同⼀公司多个APP或者不同业务;这样就有两个 相反的发展趋势:⼀⽅⾯,想共⽤模块,让多个业务共享;另⼀⽅⾯,各⾃业务⼜要隔离,独⽴发 展。公司也有可能成⽴公共的平台部。各业务部⻔之间是纵向拆分;业务和平台之间是横向拆分。 这就导致⼆维划分的⽴体架构。
- 分离出界⾯层,尽量薄,和UI同学协作,快速应变
- 分离数据层,尽量薄,与后台合作,快速应变
⼀些思考
架构设计没有统⼀的标准,上⾯接触到的架构模型,都有积极的参考意义,但是都不能照搬。需要
根据⾃⼰的实际情况进⾏⼀定的权衡取舍
- Step0:平台型应⽤
以URL的⽅式,由主App调⽤⼦App
形式类似于调⽤打电话,发短信,发邮件
URL的定义需要统筹考虑
- Step1:纵向划分
分Native,H5,插件三部分
Native和H5之间提供统⼀的桥接模块
Native和插件之间提供统⼀的桥架模块
如果加⼊ReactNative,那么也要提供Native和ReactNative之间的桥接模块。这个可以先预留,
也可以以后再添加。
- Step2:横向划分
Native部分进⾏横划分,因为这⼀块是最耗资源的部分
-
- 最上层是界⾯层(名字可以叫表现层或者UI层),这⾥可以借⽤MVVM的思想。M不⽤考虑,由下层以服务的形式提供。VM仅仅做显示逻辑,在Swift中⽤struct。这⼀层是跟产品的交流层,尽量薄,并且能够快速应对变化。业务逻辑等能分出去的功能,⼀律分出去。核⼼和重点就是让Controller只做调度者,万不得已可以酌情参与很少⼀部分的view⼯作。
-
- 最底层是微服务层(micro service)。这⼀层提供基本的功能,⽐如⽹络,缓存,加解密,系统信息,⽇志,统计等等。微服务的概念是只能供其他模块调⽤,不能调⽤其他模块的服务。本层中的模块之间也不能相互调⽤。这⾥是⼀些基础的组件,按照功能划分,相互间的隔离是第⼀考虑要素。要求⾼内聚。
-
- 中间是服务层(service),这⾥的服务可以调⽤微服务,也可以相互之间调⽤。
分三层相对简单⼀点。当然也可以分出⼀些接⼝层,服务层还可以分出公共服务层,业务逻辑层等。这个可以根据需要灵活配置。但是总体上分三层(界⾯、服务、微服务)。 不要跨层调⽤,界⾯层只能调⽤服务层提供的服务。服务层可以⾃⼰完成⼯作,也可以调⽤其他服务或者微服务完成⼯作。
-
Step3:层内划分
-
界⾯层:按照⻚⾯进⾏组织,提供公共的UI组件,可以理解为(M)VVM。VM作为将“界⾯显示”转换为“数据操作”的媒介,利⽤Swift的属性观察者特性,进⾏⼀级绑定。不引⼊RxSwift等函数式编程的⼤型第三⽅库。
-
服务层:分为公共服务,跳转逻辑,业务逻辑等模块,按照逻辑功能划分。跟具体⻚⾯不必相关,跟界⾯层的接⼝为各ViewModel种定义的协议。
-
微服务层:按照功能划分,不设计业务逻辑,分⽹络、数据库、加解密,⽇志,统计等功能框架图
语⾔选择Swift,最低⽀持版本iOS8,有条件的从iOS9开始
服务和微服务都以framework的形式提供,模块间的隔离需要重点考虑。
服务service和微服务仅仅是逻辑上的层次结构,在具体的⼯程组织上,都采⽤⼀级framework封
装,相互间的层级和调⽤采⽤相互间的依赖隐含表示。 提供⼀个界⾯隔离的service.framework,界⾯层只调⽤它完成所有任务。作⽤相当于
Foundation。
以workspace的⽅式组织⼯程,第三⽅管理⼯具采⽤Carthage。有条件的情况下,微服务以及部
分服务可以采⽤私有Carthage的形式,更⽅便复⽤。
插件也要求以framework的形式提供,不接受.a的静态库。
如果暂时需要⽤到Object-C、C、C++,都统⼀成framework的形式,以后逐步⽤Swift替换。
类图
- 界⾯层
AppDelegate、ViewController仅仅作为调度者存在,不做任何具体的事情。 ViewModel仅仅做显示逻辑相关的事情,仅仅起到将界⾯转换为数据的作⽤。⽤结构体struct,每个成员都是普通变量,并且都有默认值,代表了⻚⾯的确定性。ViewModel是⼀种数据结构,做显示逻辑的事情。
UI组件仅由View和ViewModel组成,只能包含显示逻辑,不能包含跳转逻辑,业务逻辑等。
ViewModel放UI层和Service层都有⼀定道理:放UI层表示显示逻辑;放service层表示UI和service之间的数据接⼝。考虑再三,觉得还是应该放UI层。ViewModel最⼤的作⽤还是在于将UI变化转变为数据操作,本质上离UI应该更近⼀些。
⾄于UI层和Service层之间的接⼝,还是定义相关的的ViewModel protocol⽐较好。这些protocol的定义放在service中(由于framework的影响),将ViewModel的⼀些基本需求放在protocol中。service中⽤Model或者还是⽤其他来满⾜这个protocol,就不做要求了。⽤protocol作为接⼝⽐单纯⽤Model做接⼝要好。因为Model随着后台API定义⽽变,⽽protocol只相当于⼀个基类,类型更灵活。
除了定义⼀个ViewModel的protocol之外,再定义⼀个是service的protocol,(既然引⼊service概念,就可以淡化logic和data的概念)。
界⾯层保持最轻量级。⻚⾯跳转逻辑,具体业务逻辑等⼯作全部下沉到服务层来做。 ViewModel是⼀个struct,主要做显示逻辑,概念相对⽐较⼩,⼀般⼀个⻚⾯⼀个或者多个。⽽service是⼀个类,概念⽐较⼤,可以多个⻚⾯共⽤⼀个。尺度可以根据具体情况灵活掌握。不同的⻚⾯,通过扩展遵循不同的协议区分开来。类本身可能⽐较⼤,但是每⼀部分都是相对⽐较⼩的。
- 服务层
service.framework作为⼀个粘合层存在,AppDelegate、ViewController只要import service就可以
调⽤相关服务了。
⾦融.framework、保险.framework等属于业务特有的逻辑Router、⽤户、分享等属于业务⽆关的公共逻辑
层内的各模块间可以相互调⽤具体存在形式,单例、类、或者framework等,可根据具体情况灵活决定。为了结构清晰,图中的调⽤关系线只画出了很少的⼀部分,⼤部分线都没有画出来。
- 微服务层
从开发的⻆度,按照功能分类;是⼯程师之间交流的技术语⾔,⽽不是跟产品交流的业务语⾔功能⾼内聚,作为被调⽤的基础模块模块之间不要存在相互调⽤的关系以framework的形式存在。⾼内聚,⾼复⽤。
总结
看到这里多多少少有点收获了,也许你对架构师也产生了兴趣。如果对你有所帮助的话,就帮我点点免费的赞和关注吧。最后给大家推一波资料吧。希望对你能够有所帮助。
书籍资料:下载地址