前言
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 示意图
![](https://img.haomeiwen.com/i2139357/ccd2a84fe9568245.png)
- 就 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
参考了http://www.jianshu.com/p/abea207c23e7里面给出的一个例子,这里向作者表示感谢。
先说下例子的核心思想就是:每个模块会有一个Presenter,Presenter里面包含了View(ViewController)和Model。获取数据(例如网络请求等)之后,进行View的展示和ViewController内部的逻辑处理。
下面我们结合代码来看:
首先来看项目的文件结构:
![](https://img.haomeiwen.com/i2139357/c27301eb2e313e6b.png)
代码给出了一个控制器作为例子,大家可以看到,home里面包含了四个文件夹,model、controller、presenter、view。home当中的HomePresenter是继承presenter的,HomePresenter根据业务的不同来实现自己的presenter。
网络
网络的底层还是用AFNetWorking来实现,HttpClient具体的封装大概为
![](https://img.haomeiwen.com/i2139357/8ee4cc619f807050.png)
这里说明一下,这里用delegate而不用block做回调是因为后面的HomePresenter需要对返回的数据进行处理,为了然后结构更加清晰,遵守一个函数一个功能的原则。后面还会再说一下。HttpClient提供了赋值responseHandle的init函数,外部可以通过init函数来绑定responseHandle协议。
再来看一下上面那个responseHandle这个proctocol的结构:
![](https://img.haomeiwen.com/i2139357/479b2c83756413b3.png)
目前只写了success和fail两个回调,这里为了方便演示,只写了一个参数,这个一块大伙可以根据自己的业务需求来写。
结合HttpClient来看一下,我们分别在AFNetWorking请求成功、失败的回调当中处理delegate。简单说,HttpResponseHandle就是嫁接presenter和HttpClient的协议~~
接下来看一下父类Presenter的设计。先看接口:
![](https://img.haomeiwen.com/i2139357/4523e9879ead4b97.png)
![](https://img.haomeiwen.com/i2139357/499e644734a4d341.png)
这里采用了泛型,简单说泛型就是有点类似objective-c中的id类型,大伙可以自行Google一下。父类Presenter主要是提供绑定View和解绑View的功能。
基于网络请求设计的HttpPresenter,HttpPresenter继承与Presenter,遵守HttpResponseHandle协议,并且拥有自己的泛型,HttpClient成员变量。供外部调用HttpClient,降低耦合性。
![](https://img.haomeiwen.com/i2139357/85bc39308fa8f49b.png)
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看起来就清晰多了吧
![](https://img.haomeiwen.com/i2139357/f4e00bd243fd1362.png)
在看一下controller的调用,初始化HomePresenter,然后绑定一下自己的视图,
![](https://img.haomeiwen.com/i2139357/0191085a6dced8af.png)
遵循HomeViewProtocol
![](https://img.haomeiwen.com/i2139357/6eb68ef9e026014c.png)
结尾
关于db这层还没有写好,大家可以自由发挥,按照网络这种方式,放在业务的presenter里面就可以啦,后续有时间会附上一个基于FMDB的
如果同学们有更好的方案,随时欢迎指正~
附上github地址:https://github.com/baoshanf/MVP-iOS
参考
作者:hansfeng
链接:http://www.jianshu.com/p/abea207c23e7
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
网友评论