前言
MVP模式是MVC模式的一个演化版本(好像所有的模式都是出自于MVC~~),MVP全称Model-View-Presenter。顾名思义,
Model:与MVC中的model没有太大的区别。主要提供数据的存储功能,一般都是用来封装网络获取的json数据的集合。Presenter通过调用Model进行对象交互。
View:这里的View与MVC中的V又有一些小差别,这个View可以是viewcontroller、view等控件。Presenter通过向View传model数据进行交互。
Presenter:作为model和view的中间人,从model层获取数据之后传给view,使得View和model没有耦合。
说了那么多,总得来说MVP的好处就是解除view与model的耦合,使得view或model有更强的复用性。
MVP模式下的三个特性的分析
- 任务均摊 -- 我们将最主要的任务划分到 Presenter 和 Model,而 View 的功能较少;
- 可测试性 -- 非常好,由于一个功能简单的 View 层,所以测试大多数业务逻辑也变得简单;
- 易用性 -- 代码量比 MVC 模式的大,但同时 MVP 的概念却非常清晰。
iOS MVP 示意图
- 就 MVP 而言,UIViewController 的子类实际上就是 Views 并不是 Presenters。这点区别使得这种模式的可测试性得到了极大的提高,付出的代价是开发速度的一些降低,因为必须要做一些手动的数据和事件绑定。
还有一些其他形态的 MVP -- 监控控制器的 MVP。这个变体包含了 View 和 Model 之间的直接绑定,但是 Presenter 仍然来管理来自 View 的动作事件,同时也能胜任对 View 的更新。
规范的MVP设计模式
Model 层应该不仅仅是创建一个数据对象,还应该包含网络请求,以及数据 SQLite 的 CRUD 操作(比如 iOS 平台,一般以 FMDB 框架直接操作 sql,或者用 CoreData) 。一般可以将数据对象是否需要缓存设计成一个字段 isCache,或者针对整个项目设计一个开存储关,决定整个项目是否需要数据缓存。我们常见的新闻类 App,在离线的时候看到的数据,都是做了缓存处理的。比如一些金融类的 App,实时性比较高,是不做缓存的。
View 层比较简单明了,就是 View 的一些封装、重用。在一款精心设计过的 App 里面,应该有很多 View 是可以封装重用的。比如一些自己的 TableViewCell,自己设计的 Button,一些 View(包含一些子 View,UI 精心设计过,在项目里多处出现的)等等。
Presenter 层并不涉及数据对象的网络请求和 SQLite 操作,只是 Model 层和 View 层的一个桥梁。Presenter 层就不至于太臃肿,容易看懂。一些大的 App,或因为上线时间比较久了,经历过众多程序员的修补,或因前期并未做好架构,以至于打开一个类,几千行的代码,看着自己都晕。
MVP的优势
- 模型与视图完全分离,我们可以修改视图而不影响模型
- 可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
- 我们可以将一个Presener用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
- 如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)
MVP的问题
- 由于对视图的渲染放在了Presenter中,所以视图和Persenter的交互会过于频繁.
- 还有一点你需要明白,如果Presenter过多地渲染了视图,往往会使得它与特定的视图的 联系过于紧密。一旦视图需要变更,那么 Presenter也需要变更了。比如说,原本用来呈现Html的Presenter现在也需要用于呈现Pdf了,那么视图很有可能也需要变更。
MVPDemo
参考了//www.greatytc.com/p/abea207c23e7里面给出的一个例子,这里向作者表示感谢。
先说下例子的核心思想就是:每个模块会有一个Presenter,Presenter里面包含了View(ViewController)和Model。获取数据(例如网络请求等)之后,进行View的展示和ViewController内部的逻辑处理。
下面我们结合代码来看:
首先来看项目的文件结构:
代码给出了一个控制器作为例子,大家可以看到,home里面包含了四个文件夹,model、controller、presenter、view。home当中的HomePresenter是继承presenter的,HomePresenter根据业务的不同来实现自己的presenter。
网络
网络的底层还是用AFNetWorking来实现,HttpClient具体的封装大概为
这里说明一下,这里用delegate而不用block做回调是因为后面的HomePresenter需要对返回的数据进行处理,为了然后结构更加清晰,遵守一个函数一个功能的原则。后面还会再说一下。HttpClient提供了赋值responseHandle的init函数,外部可以通过init函数来绑定responseHandle协议。
再来看一下上面那个responseHandle这个proctocol的结构:
目前只写了success和fail两个回调,这里为了方便演示,只写了一个参数,这个一块大伙可以根据自己的业务需求来写。
结合HttpClient来看一下,我们分别在AFNetWorking请求成功、失败的回调当中处理delegate。简单说,HttpResponseHandle就是嫁接presenter和HttpClient的协议~~
接下来看一下父类Presenter的设计。先看接口:
这里采用了泛型,简单说泛型就是有点类似objective-c中的id类型,大伙可以自行Google一下。父类Presenter主要是提供绑定View和解绑View的功能。
基于网络请求设计的HttpPresenter,HttpPresenter继承与Presenter,遵守HttpResponseHandle协议,并且拥有自己的泛型,HttpClient成员变量。供外部调用HttpClient,降低耦合性。
HttpPresenter.m
应用
大致就可以分为这几层了,看一下怎么应用到实例中。
上文的文件目录中可以看出我们每个功能模块都有presenter这个文件夹,对每个模块的presenter都是为这个模块服务,我们可以把请求、储存数据的活动放在这里。并且在这层presenter中处理model数据。为了使controller得到的数据能直接使用,可以多写一个protocol,来承上启下,HomeViewProtocol就为了这个产生。
@protocol HomeViewProtocol
- (void)onGetMovieListSuccess:(HomeModel*)homeModel;
- (void)onGetMovieListFail:(NSInteger) errorCode des:(NSString*)des;
@end
先看了protocol,HomePresenter看起来就清晰多了吧
在看一下controller的调用,初始化HomePresenter,然后绑定一下自己的视图,
遵循HomeViewProtocol
结尾
关于db这层还没有写好,大家可以自由发挥,按照网络这种方式,放在业务的presenter里面就可以啦,后续有时间会附上一个基于FMDB的
如果同学们有更好的方案,随时欢迎指正~
附上github地址:https://github.com/baoshanf/MVP-iOS
参考
作者:hansfeng
链接://www.greatytc.com/p/abea207c23e7
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。