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取出来生成一个单独的文件罢了。
网友评论