16 | 架构模式:为什么要选择 MVVM 模式?

[toc]

前言

本文来自拉勾网课程整理

作为iOS开发者,我们都很熟悉MVC模式。根据苹果官方的解释, MVC 表示 Model-View-Controller, 也就是模型、视图和控制器。但是业界一直把MVC戏称为 Massive ViewController(臃肿的视图控制器)。因为当我们使用 MVC的时候,随着功能越来越丰富, ViewController 往往变得臃肿和繁杂,而且模块之间相互耦合,难以维护。下图显示了苹果的 MVC 模式。

070bcbf228042085ed88a07d5e946780

其中,Controller 通常指 ViewController,是 MVC 的核心,ViewController通过Target-ActionDataSourceDelegate 来接收来自 View 的用户事件,并通过 Outlet 来更新 View。同时ViewController 还通过 NotificationKVO 来接收来自 Model的通知,并通过变量来更新 Model

除了与 ViewModel 进行交互以外,ViewController 还负责导航、网络访问、数据缓存、错误处理以及 Model 对象的EncodeDecode。由于 ViewController承担多项责任,往往导致代码量极大,且由于强耦合,对 ViewController 的一点点改动都需要进行手工回归测试,费时费力。那么,有没有什么好的办法来解决这些问题呢?

MVVM 及其优点

经过多年实践证明,MVVM 模式是目前解决臃肿 ViewController问题的有效方法。MVVM全称 Model-View-ViewModel(模型-视图-视图模型)。与 MVC相比,它引入了一个名叫ViewModel的新概念。如下图所示。

bc46ab6f120f501478f17821f6ae5da2

MVVM由三层组成:

Model,用于保存数据的模型对象,通常定义为只有数据并没有方法的结构体(Struct);

View,用于呈现 UI(用户界面)并响应用户的事件,通常是 ViewControllerView

ViewModel,用于桥接ModelView两层,把 Model转换成 View呈现 UI所需的数据,并把 View 层的用户输入数据更新到 Model中。

那么,和 MVC相比,MVVM 模式具有什么优点呢?具体有以下几个。

  • 能效减低代码的复杂性,即 MVVM 模式能有效简化 ViewController 的逻辑,使得 ViewController 的代码只处理 UI 相关的逻辑。
  • 能提高代码的可测试性,由于 ViewModel 明确负责 ModleView 之间的数据转换,而且不负责 View 的生命周期管理,我们可以很方便地测试 ViewModel 的代码逻辑,提高代码的健壮性。
  • 能够减低代码耦合性。在 MVVM 模式下,每一层都明确分工,这样可以减少代码耦合性,提高代码的可维护性和可重用性。
  • 减轻移动团队的开发门槛,方便知识的共享。前两年谷歌公司为 Android 引入了一个基于 MVVM 模式的新框架 Architecture Components,使用 MVVM模式能方便开发者同时在iOSAndroid 两个平台开发功能。

基于 MVVM 的架构设计

如今,经过多年的实践,我们已经能够成功地使用 MVVM模式在 iOSAndroid上实现了一套风格一致的架构设计。 比如,在 Moments App里面,我就进行了优化并重新实现了一套基于MVVM模式的架构,具体如下图所示:

c40bb1e334ea9605a604e5877702f461

Moments App 的架构详解

以上是我们 Moments App 的架构图,下面我把每一层进行分解和介绍下。

View 层

View 层由UIViewController以及UIView所组成,负责呈现UI和响应用户事件。由于 MVVM 模式相当灵活,我们在后面会介绍如何在保持其他模块完全不变的情况下把View层换成 SwiftUI 来实现。

ViewModel 层

它是 MVVM 模式的核心,主要任务是连接 ViewModel层,为 View层准备呈现UI所需的数据,并且响应 View 层所提供的用户事件。同时它还负责处理路由和发送用户行为数据。由于 ViewModel层的责任还是很重,因此我们把它进一步细分为各个模块,大致包括ViewModel、Routing、Tracking、Repository、Networking、DataStore

其中,ViewModel扮演一个协助者的角色,负责准备 View 层所需的数据,并响应 View 层的用户事件。ViewModel 与上层的UIViewControllerUIView之间通过响应式编程的方式来通知对方,而上层 UI组件通过数据绑定的方式,来监听 ViewModel 的数据变化,并及时更新 UI

Routing是负责路由和导航的模块,ViewModel 在响应 View 的请求时(如打开新页面),会调用 Routing 模块进行导航。

Tracking则负责统计分析数据的模块,ViewModel在响应 View 的请求时(如用户点击了点赞按钮),会调用 Tracking 模块来发送用户行为的数据。

Repository是数据层,作为唯一信息来源(Single source of truth)维护着 App所使用的Model 数据。当 ViewModel需要访问数据的时候,会调用 Repository 模块,然后Repository会根据需要通过 Networking 来访问网络的后台数据,或者调用 DataStore 来获取本地缓存的数据。ViewModelRepository的接口也是响应式编程的方式,主要由 ViewModelRepository发起请求,然后监听 Repository来获取数据所发生的变化。

Networking是网络层,负责访问 BFF,然后把 BFF 返回的 JSON数据 DecodeModel数据。RepositoryNetworking 的接口也是响应式编程的方式。Repository 会给 Networking 发起请求,并监听 Networking 的返回。

DataStore为数据存储层,用于把数据缓存到App里面使得用户在不需等待网络请求的情况下可以快速看到 UIRepositoryDataStore 的接口也是响应式编程的方式。Repository 监听 DataStore 的数据变化。

Model 层

最后一层是 Model层,它比较简单,都是一些用于存放数据的模型对象,这些对象能用于存放网络请求使用的数据,也可用于存放本地缓存的数据。

朋友圈模块的架构设计

有了上述的架构设计,我就可以把MVVM模式应用到各个业务模块里面。比如,在这里我以 Moments App的朋友圈模块作为例,和你介绍下它的具体的架构。下面是我为该模块所绘制的类型关系图。

5938373470e8a4022575484baa56321d

View 层用于显示 UIMomentsViewTimelineViewController用于显示朋友圈界面,这个页面里面使用了一个 TableView 来显示各种不同的Cell。这些Cell都通过UIView来显示,其中UserProfileListItemView用于显示用户个人的信息,而MomentListItemView显示一条朋友圈的信息。

ViewModel 层由多个组件所组成。其核心是MomentsTimelineViewModel,它为MomentsViewTimelineViewController准备呈现UI的数据,并处理用户的事件。各个UI子组件也对应着各个子 ViewModel。例如UserProfileListItemView对应了UserProfileListItemViewModel

MomentsTimelineViewModel要发送统计分析数据的时候会调用TrackingRepo,进而把用户行为数据发送到分析数据的后台。当MomentsTimelineViewModel要导航到其他页面的时候会调用AppRouter,而AppRouter会调用路由模块来导航到相应的页面去。

MomentsTimelineViewModel需要读取或者更新数据的时候,会给MomentsRepoo发起请求。该 Repo 负责发送网络请求并把朋友圈数据缓存到本地文件系统中。这个 Repo 还是所有朋友圈信息的数据中心,App 里面任何页面需要朋友圈信息的数据,都可以从该 Repo 中进行读取。

为了进一步解耦,我们把数据缓存和网络请求模块从Repo中独立出来,分成 DataStoreNetworking两个模块。例如当MomentsRepoo需要读写缓存的时候,会调用UserDefaultsPersistentDataStoreDataStore模块负责把模型数据保存到UserDefaults里面。

而当MomentsRepoo需要从网络上取出朋友圈信息时会调用GetMomentsByUserIDSessionGetMomentsByUserIDSession会从 BFF 里读取当前用户所有的朋友圈信息。当MomentsRepoo需要更新朋友圈信息时(如更新点赞的状态),会调用UpdateMomentLikeSession来对 BFF 发起更新的请求。

Model 层相对比较简单,只有一个用于保存朋友圈信息的模型数据:MomentsDetails。其中UserDefaultsPersistentDataStore使用它来进行本地缓存的读取,而GetMomentsByUserIDSessionUpdateMomentLikeSession会使用它来存放网络请求的JSON 数据。

上面就是 Moments App 基于 MVVM模式的架构设计,我们会在后面几章中详细介绍各个层的架构与具体实现方式。

总结

结合 Moments App· 介绍了一套可行的 ·MVVM· 架构模式。通过它,我们可以有效降低各个模块之间的耦合度,提高可重用性,再加上由于每个模块有了明确的责任与分工,我们在实现新功能时,也能降低沟通成本,提高开发效率。所以有关这个架构模式,你一定要重视起来哦。

另外,结合BFF 的后端服务,我们还可以在iOSAndroid两端同样使用 MVVM模式来进行架构设计,并保持一致的代码风格,以便于开发者能同时在两个平台进行开发。

你可能感兴趣的:(16 | 架构模式:为什么要选择 MVVM 模式?)