写在前面
这两天时间宽松一点,所以我也来学学MVP的设计模式,有人说是架构,管他呢,好用就行。so,开干! 后边有OC版本的demo
打开了Google,搜了一下关键字 MVP iOS,然后发现了一些文章,最后是这篇文章带给我对MVP 的一些认识。
MVP似乎有好多的变种,作者所说的这种有如下特点:
(原文如下:)
the view part of the MVP consists of both UIViews and UIViewController
the view delegates user interactions to the presenter
the presenter contains the logic to handle user interactions
the presenter communicates with model layer, converts the data to UI friendly format, and updates the view
the presenter has no dependencies to UIKit
the view is passive (dump)
稍微翻译一下
MVP的 V 层是由UIViewController 和UIView 共同组成
view 将委托presenter 对它自己的操作,(简单来说就是presenter发命令来控制view的交互,要你隐藏就隐藏,叫你show 你就乖乖的show)
presenter拥有对 view交互的逻辑(就是上面说的意思)
presenter跟model层通信,并将数据转化成对适应UI的数据并更新view
presenter不需要依赖UIKit
view层是单一,因为它是被动接受命令,没有主动能力
presenter 作为业务逻辑的处理者,首先要向model层拿数据,所以它将可以向model层通信。其次,UI的处理权移交给了它,所以它需要与view成通讯,发送命令改变UI。同时,UI的响应将触发业务逻辑的处理,所以view 层向presenter层通讯,告诉他用户做了什么操作,需要你反馈对应的数据来更新UI。这样就完成了从用户交互获得交互反馈到整个业务逻辑。
先看看结构
UserViewProtocol 协议定义了一下方法,这些方法其实就是presenter对view层发送的命令
#import <Foundation/Foundation.h>
@protocol UserViewProtocol <NSObject>
-(void) userViewDataSource:(NSArray*)data;
-(void) showIndicator;
-(void) hideIndicator;
-(void) showEmptyView;
@end
UserService 类是用来请求数据给presenter的,也能算是model层吧。我就只定义了一个方法。
-(void)getUserInfosSuccess:(SuccessHandler )success andFail:(FailHandler) failure
这一层,其实也可以很复杂,这就涉及网络层的架构了
ViewController类则是UI层,它实现了tableview自己的协议,还实现了用户交互的协议 UserViewProtocol,也就说,presenter向UI层发送命令,其实是发给UI层的viewController,实际上是控制器来被动的更新UI。这个不管是MVC还是MVP,view的实际控制权应该都是viewController,这个理解应该没错吧。
协议中的几个方法实现如下。
-(void)userViewDataSource:(NSArray*)data{
self.friendlyUIData = data;
[self.tableview reloadData];
}
-(void) showIndicator{
self.indicator.hidden = NO;
}
-(void) hideIndicator{
self.indicator.hidden = YES;
}
-(void) showEmptyView{
UIAlertController *alertView = [UIAlertController alertControllerWithTitle:@"Alert" message:@"show empty view" preferredStyle:UIAlertControllerStyleAlert];
[alertView addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alertView animated:YES completion:^{
}];
}
另外,在viewDidload中,调用了presenter的两个public 方法
self.presenter = [Presenter new];
[self.presenter attachView:self];
[self.presenter fetchData];
那就来看看Presenter类,这个类我在demo中加了不少的注释,主要是为了理清自己的思路
先说attachView 方法,这个方法是对外公开的,目的就是为了将实现了UserViewProtocol 协议的对象(其实应该就是控制器,因为view的直接操作者就是view Controller)绑定到presenter 上,说白了就是presenter 可以直接拿到实现了UserViewProtocol 协议的对象,并且向他发送命令(协议实现的方法,前面有说到),具体该方法的实现
@interface Presenter()
@property (nonatomic,strong) UserService *userService;
@property (nonatomic,weak) id<UserViewProtocol> attachView;
@end
-(void)attachView:(id <UserViewProtocol>)view{
self.attachView = view;
self.userService = [UserService new];
}
注意这里用了weak 来修饰attachView 树形,因为presenter和 viewController相互持有,所以必须要通过weak 来打破循环引用,这跟我们平时使用委托协议( delegate)是一样的,只是名字换成了attachView 而已。
再来说fetchData方法,公开这个方法,只是为了数据请求有个统一的接口,而不需要presenter分开多次调用,presenter自己处理所有事情,不让viewController参和进来。具体实现就看demo
最后说一下
-(NSArray *)processOriginDataToUIFriendlyData:(NSArray *) originData{
NSMutableArray *friendlyUIData = [NSMutableArray new];
for (NSDictionary *dic in originData) {
if ([[dic valueForKey:@"gender"] isEqualToString:@"males"]) {
[friendlyUIData addObject:dic];
}
}
return friendlyUIData;
}
这个私有方法是将原始数据转换成UI所需要的数据,这样UI拿到数据就可以直接使用,而不用做各种判断,逻辑依然放在了presenter中。而且,这个数据处理可以做成协议,输出不同UI需要的数据,这个也可以看casatwy大神 关于view架构的文章。