iOS 之 MVP 框架

作者: 后浪普拉斯 | 来源:发表于2018-10-09 13:45 被阅读0次

MVC

先说一下传统的mvc,借用一张网上MVC的框架的图片


image.png

这大概是我们理想的方式,
controller 负责控制,view 负责展示,Model负责数据
理想很丰满现实很骨感啊!真实的MVC 却是这样的:


image.png

Controller和View 却是冗杂在一起的,这样的好处就是上手较快,不用去想什么东西,直接在ViewController里面开干,总结起来:简单粗暴,但是简单粗暴的后果就是维护起来简单,单元测试不能实现,就连查看也需要滚动一会,究其原因就是controller实在承载了太多的东西,从业务逻辑到UI布局,从按钮点击到请求数据,使的VC和V太大,于是就衍生出其他的框架,用来分离VC和V。

MVP

MVC 的架构:


image.png

看起来和MVC 有异曲同工之意。
那就下来废话不多说,直接上代码。
先看一下工程的目录结构:


image.png
YLeUserPresenter.h
#import "YLePresenter.h"//继承父类

//生成代理协议
@protocol YLeUserPresenterProtocol <NSObject>
//生成代理方法
-(void)onGetUserInfoSuccess:(id)model failure:(NSError *)error;

@end
//实现代理协议
@interface YLeUserPresenter : YLePresenter<YLeUserPresenterProtocol>
//<id<YLeUserPresenterProtocol>>
-(void)getUserInfo;

@end
YLeUserPresenter.m
#import "YLeUserPresenter.h"
#import "YLePresentModel.h"


@implementation YLeUserPresenter

-(void)getUserInfo{
//此处可以是去通过网络获取数据,并将数据转换成model
    YLePresentModel *model = [[YLePresentModel alloc] init];
    model.name = @"Alex";
    model.age = 18;
//判断绑定的_view 是否实现了代理方法,实现了,就可以继续调用代理方法
    if ([_view respondsToSelector:@selector(onGetUserInfoSuccess:failure:)]) {
        [_view onGetUserInfoSuccess:model failure:nil];
    }
}
@end
YLePresentModel.h
#import <Foundation/Foundation.h>

@interface YLePresentModel : NSObject

@property(nonatomic, strong) NSString *name;
@property(nonatomic, assign) int age;

@end

此时的model只是单传的model,在其中没有逻辑,这是瘦model,我们也可以将网络访问放到其中,这样有些业务逻辑便可以放倒model中,这是胖model。

接下来我们查看MVP的重点,我们定义了代理,但是如何调用呢?

YLePresentView.h
#import <UIKit/UIKit.h>
@class YLePresentModel;

@interface YLePresentView : UIView
//调用初始化方法
-(instancetype)initWithFrame:(CGRect)frame;

//model 修改页面配置
-(void)setViewWithModel:(YLePresentModel *)model;

@end
YLePresentView.m 重点
#import "YLePresentView.h"
#import "YLePresentModel.h"
#import "YLeUserPresenter.h"

@interface YLePresentView()<YLeUserPresenterProtocol>
@property(nonatomic, strong) UILabel *nameLabel;
@property(nonatomic, strong) UILabel *ageLabel;
@property(nonatomic, strong) UIButton *getInfoBtn;
@property(nonatomic, strong) YLeUserPresenter *userPresenter;
@end

@implementation YLePresentView

-(instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self addSubViews];
    }
    return self;
}

-(void)addSubViews{
    self.backgroundColor = [UIColor whiteColor];
    
    self.nameLabel = [[UILabel alloc] init];
    self.nameLabel.frame = CGRectMake(100, 100, 80, 30);
    [self.nameLabel setFont:[UIFont systemFontOfSize:15]];
    [self.nameLabel setBackgroundColor:[UIColor grayColor]];
    [self.nameLabel setTextColor:[UIColor redColor]];
    [self addSubview:self.nameLabel];
    
    self.ageLabel = [[UILabel alloc] init];
    self.ageLabel.frame = CGRectMake(100, 150, 80, 30);
    [self.ageLabel setFont:[UIFont systemFontOfSize:15]];
    [self.ageLabel setBackgroundColor:[UIColor grayColor]];
    [self.ageLabel setTextColor:[UIColor orangeColor]];
    [self addSubview:self.ageLabel];
    
    self.getInfoBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    self.getInfoBtn.frame = CGRectMake(100, 200, 80, 30);
    [self.getInfoBtn setBackgroundColor:[UIColor redColor]];
    [self.getInfoBtn setTitle:@"Get Info" forState:UIControlStateNormal];
    self.getInfoBtn.titleLabel.font = [UIFont systemFontOfSize:15];
    [self.getInfoBtn addTarget:self action:@selector(getUserInfo) forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:self.getInfoBtn];
}

-(void)getUserInfo{
    [self.userPresenter getUserInfo];
}


-(YLeUserPresenter *)userPresenter{
    if (!_userPresenter) {
        _userPresenter = [[YLeUserPresenter alloc] initWithView:self];
    }
    return _userPresenter;
}

-(void)setViewWithModel:(YLePresentModel *)model{
    self.nameLabel.text = [NSString stringWithFormat:@"name: %@", model.name];
    self.ageLabel.text = [NSString stringWithFormat:@"age: %d",model.age];
}

-(void)onGetUserInfoSuccess:(id)model failure:(NSError *)error{
    if ([model isKindOfClass:[YLePresentModel class]]) {
        [self setViewWithModel:model];
    }
}

分析:

我们可以看到这个其中View 生成了一个presenter的对象并持有,而且自身也继承了YLeUserPresenterProtocol协议,我们在使用presenter对象调用自身的方法getUserInfo ,会发现getUserInfo 中会调用当前View(继承YLeUserPresenterProtocol),我们发现程序数据流转到View中来,此时代理方法被调用,实现界面数据的完成。
此时我们回看上面的代码,此时MVP的架构变成了


image.png

但是我们也可以在controller中实现上面的东西,这是和上面view中的实现有些重复的。

ViewController.m
#import "ViewController.h"
#import "YLePresentView.h"
#import "YLeUserPresenter.h"
#import "YLePresentModel.h"

@interface ViewController ()<YLeUserPresenterProtocol>

@property(nonatomic, strong) YLePresentView *mainView;
@property(nonatomic, strong) YLeUserPresenter *userPresenter;
@end

@implementation ViewController


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self.view addSubview:self.mainView];
    [self.userPresenter getUserInfo];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)onGetUserInfoSuccess:(id)model failure:(NSError *)error{
    if ([model isKindOfClass:[YLePresentModel class]]) {
        [self.mainView setViewWithModel:model];
    }
}


-(YLePresentView *)mainView{
    if (!_mainView) {
        //CGRectMake(0, 0, 375 ,667 )
        _mainView = [[YLePresentView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    }
    return _mainView;
}

-(YLeUserPresenter *)userPresenter{
    if (!_userPresenter) {
        _userPresenter = [[YLeUserPresenter alloc] initWithView:self];
    }
    return _userPresenter;
}

@end

我们可以发现此事VC拥有View 和 Presenter,通过Presenter对象调用自身方法,实现代理调用自身实现的代理方法,从而实现View和Controller的分离,但是就个人而言,此框架有些绕,通过代理对象调用自身方法,调用VC或者V继承的代理协议的方法。


注意:网上有些人讲的比较复杂,其实就是通过调用代理协议来查询数据,之后将数据填充到UI布局中,没有那么神奇,还有些人单独生成一个代理协议的文件,其实就是将本文中的presenter中的Protocol取出来生成一个单独的文件罢了。


相关文章

网友评论

    本文标题:iOS 之 MVP 框架

    本文链接:https://www.haomeiwen.com/subject/iheeaftx.html