我们常见的分层架构,
三层架构:视图层,业务层,数据层
四层架构:视图层,业务层,网络层,本地数据层
1.视图层设计方案
2.网络层设计方案
3.本地持久化方案
4.动态部署方案
上面这四大点,稍微细说一下就是:
页面如何组织,才能尽可能降低业务方代码的耦合度?尽可能降低业务方开发界面的复杂度,提高他们的效率?
如何让业务开发工程师方便安全地调用网络API?然后尽可能保证用户在各种网络环境下都能有良好的体验?
当数据有在本地存取的需求的时候,如何能够保证数据在本地的合理安排?如何尽可能地减小性能消耗?
iOS应用有审核周期,如何能够通过不发版本的方式展示新的内容给用户?如何修复紧急bug?
一个好的架构
遵循代码规范代码,分类明确(没有难以区分模块的文件夹或模块)
注释明了, 逻辑清晰, 不用文档,或很少文档,就能让业务方上手
思路和方法要统一,尽量不要多元
没有横向依赖,尽可能少的跨层访问
对业务方该限制的地方有限制,该灵活的地方要给业务方创造灵活实现的条件
易测试,易拓展
保持一定量的超前性
接口少,接口参数少
低内存,高性能
--------------------------视图层设计方案 --------------------------
1.制定代码规范:可读性,可维护性,传承性
2.view的布局:code/xib/storyboard的选择
3.是否有必要统一派生基类ViewController(替代方法AOP切面编程)
4.架构模式(mvc/mvvm/mvp)
5.减少横向依赖和跨层访问:让依赖关系下沉,引入Mediator模式,中介者模式
----------------------------网络层设计方案-------------------
1.网络层跟业务对接部分的设计
a.数据传递方式选择:Delegate为主,Notification为辅
b.尽可能减少跨层数据交流的可能,限制耦合
c.为什么尽量不要用block(1.很难追踪,难以维护2.会延长相关对象的生命周期3.delegate弱引用)
d.统一回调方法,便于调试和维护
2.网络层的安全机制实现
a.设计秘钥
b.https
3.网络层的优化方案
网络层的优化手段主要从以下三方面考虑:
1.针对链接建立环节的优化
在API发起请求建立链接的环节,大致会分这些步骤:
1.发起请求
2.DNS域名解析得到IP
3.根据IP进行三次握手(HTTPS四次握手),链接建立成功
1.使用缓存手段减少请求的发起次数
对于大部分API调用请求来说,有些API请求所带来的数据的时效性是比较长的,比如商品详情,比如App皮肤等。那么我们就可以针对这些数据做本地缓存,这样下次请求这些数据的时候就可以不必再发起新的请求。
再比如网络图片缓存,数据量基本上都特别大,这种就比较适合针对缓存大小来清理缓存的策略。
另外,之前的缓存的前提都是基于内存的。我们也可以把需要清理的缓存存储在硬盘上(APP的本地存储,我就先用硬盘来表示了,虽然很少有手机硬盘的说法,哈哈),比如前面提到的图片缓存,因为图片很有可能在很长时间之后,再被显示的,那么原本需要被清理的图片缓存,我们就可以考虑存到硬盘上去。当下次再有显示网络图片的需求的时候,我们可以先从内存中找,内存找不到那就从硬盘上找,这都找不到,那就发起请求吧。
2.使用策略来减少请求的发起次数
这个我在前面提到过,就是针对重复请求的发起和取消,是有对应的请求策略的。我们先说取消策略。
如果是界面刷新请求这种,而且存在重复请求的情况(下拉刷新时,在请求着陆之前用户不断执行下拉操作),那么这个时候,后面重复操作导致的API请求就可以不必发送了。
如果是条件筛选这种,那就取消前面已经发送的请求。虽然很有可能这个请求已经被执行了,那么取消所带来的性能提升就基本没有了。但如果这个请求还在队列中待执行的话,那么对应的这次链接就可以省掉了。
以上是一种,另外一种情况就是请求策略:类似用户操作日志的请求策略。
用户操作会触发操作日志上报Server,这种请求特别频繁,但是是暗地里进行的,不需要用户对此有所感知。所以也没必要操作一次就发起一次的请求。在这里就可以采用这样的策略:在本地记录用户的操作记录,当记录满30条的时候发起一次请求将操作记录上传到服务器。然后每次App启动的时候,上传一次上次遗留下来没上传的操作记录。这样能够有效降低用户设备的耗电量,同时提升网络层的性能。
3.针对DNS域名解析做的优化,以及建立链接的优化
基于以上三个原因所导致的最终结果就是,API请求在DNS解析阶段的耗时会很多。
那么针对这个的优化方案就是,索性直接走IP请求,那不就绕过DNS服务的耗时了嘛。
方案就应该是这样:本地有一份IP列表,这些IP是所有提供API的服务器的IP,每次应用启动的时候,针对这个列表里的所有IP取ping延时时间,然后取延时时间最小的那个IP作为今后发起请求的IP地址。
2.针对链接传输数据量的优化
这个很好理解,传输的数据少了,那么自然速度就上去了。这里没什么花样可以讲的,就是压缩呗。各种压缩。
3.针对链接复用的优化
建立链接本身是属于比较消耗资源的操作,耗电耗时。SPDY自带链接复用以及数据压缩的功能,所以服务端支持SPDY的时候,App直接挂SPDY就可以了。如果服务端不支持SPDY,也可以使用PipeLine,苹果原生自带这个功能。
不过目前业界趋势是倾向于使用HTTP/2.0来代替SPDY,不过目前HTTP/2.0还没有正式出台,相关实现大部分都处在demo阶段,所以我们还是先SPDY搞起就好了。未来很有可能会放弃SPDY,转而采用HTTP/2.0来实现网络的优化。这是要提醒各位架构师注意的事情。嗯,我也不知道HTTP/2.0什么时候能出来。
------------------------------动态部署方案-------------------
通过不发版的方式,将新的内容、新的业务流程部署进已发布的App
1.webApp
2.Hybrid App
3.React-Native
5.Javascript Patch
总结:架构模式 动态部署至少满足以下要求
1.View和事件都要能够动态部署
2.功能完整
3.便于维护
我更加倾向于H5和Native以JSBridge的方式连接的方案进行动态部署,在cocoapods里面也有蛮多的JSBridge了
-------------------------------本地持久化方案----------------------
持久化方案不管是服务端还是客户端,都是一个非常值得讨论的话题。尤其是在服务端,持久化方案的优劣往往都会在一定程度上影响到产品的性能。所以我在移动端这边做持久化方案设计的时候,考虑更多的是方案的可维护和可拓展,然后在此基础上才是性能调优。
持久化方案对整个App架构的影响和网络层方案对整个架构的影响类似,一般都是导致整个项目耦合度高的罪魁祸首。而我也是一如既往的去Model化的实践者,在持久层去Model化的过程中,我引入了Virtual Record的设计,这个在文中也会详细描述。
这里主要讲以下几点:
1.根据需求决定持久化方案
1)NSUserDefault
2)Plist、archive、Stream
3)Core Data
4)SQlite
5) keychain
2.持久层与业务层之间的隔离
在设计持久层架构的时候,我们要关注以下几个方面的隔离:
1.持久层与业务层的隔离
2.数据库读写隔离
3.多线程控制导致的隔离
4.数据表达和数据操作的隔离
3.持久层与业务层的交互方式
在交互方案的设计中,架构师应当区分好强弱业务,把传统的Data Model区分成Table和Record,并由DataCenter去实现强业务,Table去实现弱业务。在这里由于DataCenter是强业务相关,所以在实际编码中,业务工程师负责创建DataCenter,并向业务层提供业务友好的方法,然后再在DataCenter中操作Table来完成业务层交付的需求。区分强弱业务,将Table和Record拆分开的好处在于:
1.通过业务细分降低耦合度,使得代码迁移和维护非常方便
2.通过拆解数据处理逻辑和数据表达形态,使得代码具有非常良好的可拓展性
3.做到读写隔离,避免业务层的误操作引入Bug
4.为Virtual Record这一设计思路的实践提供基础,进而实现更灵活,对业务更加友好的架构
任何不区分强弱业务的架构都是架构师在耍流氓
在具体与业务层交互时,采用Virtual Record的设计思路来设计Record,由具体的业务对象来实现Virtual Record,并以它作为DataCenter和业务层之间的数据媒介进行交互。而不是使用传统的数据模型来与业务层做交互。
4.数据迁移方案
一般来说,具有持久层的App同时都会附带着有版本迁移的需求。当一个用户安装了旧版本的App,此时更新App之后,若数据库的表结构需要更新,或者数据本身需要批量地进行更新,此时就需要有版本迁移机制来进行这些操作。然而版本迁移机制又要兼顾跨版本的迁移需求,所以基本上大方案也就只有一种:建立数据库版本节点,迁移的时候一个一个跑过去。
数据迁移事实上实现起来还是比较简单的,做好以下几点问题就不大了:
1.根据应用的版本记录每一版数据库的改变,并将这些改变封装成对象
2.记录好当前数据库的版本,便于跟迁移记录做比对
3.在启动数据库时执行迁移操作,如果迁移失败,提供一些降级方案
在版本迁移时要注意的一点是性能问题。我们一般都不会在主线程做版本迁移的事情,这自然不必说。需要强调的是,SQLite本身是一个容错性非常强的数据库引擎,因此差不多在执行每一个SQL的时候,内部都是走的一个Transaction。当某一版的SQL数量特别多的时候,建议在版本迁移的方法里面自己建立一个Transaction,然后把相关的SQL都包起来,这样SQLite执行这些SQL的时候速度就会快一点。
5.数据同步方案
1.单向数据同步:只把本地较新数据同步到服务器,不会从服务器拉取(identifier)
2.双向数据同步: 笔记类,日程类应用(每次唤醒拉取一次,对实时性要求比较高的,要么客户端本地起一个线程做轮询,要么服务器通过长链接将待执行操作推送过来)