iOS开发-基类设计:架构模式之MVC、MVP、MVVM架构
概述
最近在改造项目中的基类设计,侧重注意了一下基类的设计和使用。
现存问题:
ViewController承担任务过多,不易扩展变化,列表数据处理存在资源浪费
1. 列表中已无更多数据还会进行网络请求,浪费资源
2. 列表已经有可用数据,再进行第N页请求失败了,都会展示网络加载失败页面,无法展示之前有效数据
3. 基类做了上拉下拉数据默认获取,有一定的方便性,但是也影响了数据的灵活组装
4. 下拉加载、上拉获取,不同业务也有不确定性,不够通用的业务放在基类中反而增加了很多判断
5. ViewController更新取决于监听ViewModel对于数据获取完后对Key改变
ViewModel业务承载过于单一,对于复杂业务无法进行拆分组装
1. 只能承载单个列表请求,业务复杂的列表,无法进行拆分后组装
2. 网络请求分散于VM C各个地方,不便于统一开发规范
改造目的:
聚合大量共有的常用业务逻辑,极大程度减少开发者冗余代码的产生,更加专注于自身模块的开发
采用协议的方式,让其他类可以去实现,一定程序上保证了开发规范
架构设计
关于基类架构设计,基于项目本身也是基于MVVM的模式,目前也准备沿用下去,对应着也了解一下Model、View、Controller/Presenter/ViewModel之间的关系,以及这三者组合而形成的多种设计模式:
MVC(传统MVC、Cocoa MVC)
MVP
MVVM
Model
"Model objects encapsulate the data specific to an application and define the logic and computation that manipulate and process that data. For example, a model object might represent a character in a game or a contact in an address book. A model object can have to-one and to-many relationships with other model objects, and so sometimes the model layer of an application effectively is one or more object graphs. Much of the data that is part of the persistent state of the application (whether that persistent state is stored in files or databases) should reside in the model objects after the data is loaded into the application. Because model objects represent knowledge and expertise related to a specific problem domain, they can be reused in similar problem domains. Ideally, a model object should have no explicit connection to the view objects that present its data and allow users to edit that data—it should not be concerned with user-interface and presentation issues."
模型 - 封装特定于应用程序的数据,可以是单个对象,也可以是对象的某种结构,并定义操作和处理该数据的逻辑和计算。理想状态下,model与显示其数据或编辑其数据视图都没有直接关系,它不关心用户界面和展示情况
View
“A view object is an object in an application that users can see. A view object knows how to draw itself and can respond to user actions. A major purpose of view objects is to display data from the application’s model objects and to enable the editing of that data. Despite this, view objects are typically decoupled from model objects in an MVC application.
Because you typically reuse and reconfigure them, view objects provide consistency between applications. Both the UIKit and AppKit frameworks provide collections of view classes, and Interface Builder offers dozens of view objects in its Library.”
视图 - 作为UI的视觉展示,通常会突出model的某些属性,也会隐藏其他属性。View应明确知道该如何根据Model中的对象属性绘制自己,并响应用户操作对数据进行编辑。
虽然View看起来与Model应该是强关联,但通常View与Model是解耦的
Controller/Presenter/ViewModel
“A controller object acts as an intermediary between one or more of an application’s view objects and one or more of its model objects. Controller objects are thus a conduit through which view objects learn about changes in model objects and vice versa. Controller objects can also perform setup and coordinating tasks for an application and manage the life cycles of other objects.”
控制器 - 通常作为View与Model的中介,View通过其知道Model的变化,对应更新视图变化,Model通过其获知用户操作后需要进行的数据更新。
Model-View-Controller(MVC)
old_mvc
View从Model中获取数据的变化,进行状态更新
Model负责数据管理,通知View更新UI
Controller响应用户输入并对数据模型对象执行更新
简单来看,其符合View、Model、Controller特性设计,但三者之间均存在通讯,紧密耦合,大大降低了三者之间的复用性。
基于传统MVC过于古老,且不太实用于当下的iOS开发,苹果设计出一套Cocoa MVC模式,进行解耦View 与Model
愿景
Controller作为中介器协调View与Model,View与Model完全解耦
cocoa_mvc
实际
Controller需要承担代理、数据源、分发取消网络和一系列其他的任务,通常过于臃肿
View
只作为传递用户动作事件而存在
cocoa_mvc2
Controller与View是紧密耦合的,直接导致对他们进行测试是很艰难的(需要模拟View和它们的生命周期),业务代码也逐渐的被分散到View布局代码中,View的复用也变得极其艰难,当有类似View出现时,要么我们将在View中写很多逻辑控制,要么我们将舍弃复用而再维护一个类似的View,造成很多的代码冗余
综上
1. View
与Model进行了解耦,Model可方便进行单元测试和复用
2. Controller
与View紧密耦合,Controller承担任务过重,过于臃肿
3.
不熟悉的开发人员也能快速上手
MVP是基于MVC架构模式的一种派生模式,更加适用于构建用户交互,旨在促进自动化单元测试。
MVP
定义出一种“中介者”角色,所有的协调更新逻辑都可交由Present来处理
- Model
定义要在用户界面中显示以及操作的数据接口
- View
由Model驱动显示数据并将用户操作事件告知给“中介者”
- Presenter
根据模型和视图进行操作,从Model中检索数据,并将其进行处理后驱动在View中展示
MVP
看起来MVP是更加符合苹果愿景的用户界面架构模式,Presenter在此只负责更新View的数据和状态,并不关注Controller的生命周期,就MVP而言,如果把Controller的子类看作Views,我们的可测试性将极大的提高,但相应的开发速度可能会有一些降低,因为必须要做一些手动的数据和事件的绑定。
View/Controller与Model解耦,功能简单,便于测试
代码量大,所有的更新传递数据操作都要通过Presenter,但是概念清晰,职责明确
MVVM 同样也是一种派生自MVC的设计模式,它将用户界面展示与后端逻辑分离开来,ViewModel作为一个转换器,负责管理Model的转换以及View的更新,用于处理大部分视图的显示逻辑、Model的转换逻辑、对于后端数据的访问逻辑
Q:ViewModel在iOS中ViewModel实际上代表什么?
A:它基本上就是UIKit下的每个控件以及控件的状态。ViewModel调用会改变Model同时会将Model的改变更新到自身并且因为我们绑定了View和ViewModel,第一步就是相应的更新状态。
MVVM
Model 数据对象(数据处理可通过自身提供接口,也可添加分类来扩展业务逻辑)
View(View+Controller),负责UI展示,绑定viewModel,触发viewModel中提供的命令以及呈现viewModel提供的数据
View-Model 负责从Model获取View所需的数据,转换成View可以展示的数据,并暴露公开的属性和命令提供给view进行绑定
Binder 在MVVM中,声明性数据和命令绑定是隐式的。它可以使开发者非常方便的实现View与ViewModel的同步,避免编写大量烦杂的样板话的代码。
iOS中基于Binder的两种方案:
基于KVO的绑定库如RZDataBinding和SwiftBond
完全的函数响应式编程,比如像ReactiveCocoa、RxSwift或者PromiseKit
MVVM模块层级图
MVVM模块层级图
综上特性
任务均摊 -- 在例子中并不是很清晰,但是事实上,MVVM的View要比MVP中的View承担的责任多。因为前者通过ViewModel的设置绑定来更新状态,而后者只监听Presenter的事件但并不会对自己有什么更新。
可测试性 -- ViewModel不知道关于View的任何事情,这允许我们可以轻易的测试ViewModel。同时View也可以被测试,但是由于属于UIKit的范畴,对他们的测试通常会被忽略。
易用性 -- 必须把View中的事件指向Presenter并且手动的来更新View,如果使用绑定的话,MVVM代码量将会小的多。
参考文档