一起养成写作习气!这是我参与「日新方案 4 月更文应战」的第9天,点击检查活动概况。
MVP
面向协议编程,即Model View Presenter
(模型 视图 协调器)。
特征
- 使命均摊:将最主要的使命划分到
Presenter
和Model
,而View
的功用较少 - 可测验性:由于一个功用简略的
View
层,所以测验数事务逻辑变得简略 - 易用性:比运用
MVC
形式的代码量更多,但MVP
形式具备非常明晰的结构
所以,MVC
与MVP
的差异便是,在MVP
中Model
和View
没有直接通信。
规划形式
-
Model
层不仅仅是创立一个数据目标,还应该包含网络恳求,以及数据Cache
的操作 -
View
层便是一些封装、重用 -
Presenter
层并不涉及数据的网络恳求和Cache
操作,仅仅搭建Model
层和View
层的一个桥梁
优势
- 模型与视图彻底分离,修正视图而不影响模型
- 能够更高效地运用模型,由于一切交互都在
Presenter
中 - 把逻辑放在
Presenter
中,能够脱离用户接口进行单元测验 - 能够将一个
Presener
用于多个视图,而不需要改动Presenter
的逻辑。这个特性非常的有用,由于视图的改变总是比模型的改变频繁
UI
与Model
解耦
创立LGProtocol
,完结Cell
中按钮点击的事情接口
#import <Foundation/Foundation.h>
@protocol LGProtocol <NSObject>
- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath;
@end
在Present
中,完结LGProtocol
协议的didClickNum
办法
#import <Foundation/Foundation.h>
#import "Model.h"
#import "LGProtocol.h"
#import <YYKit.h>
@interface Present : NSObject<LGProtocol>
@property (nonatomic, strong) NSMutableArray *dataArray;
@property (nonatomic, weak) id<LGProtocol> delegate;
@end
@implementation Present
- (instancetype)init{
if (self = [super init]) {
[self loadData];
}
return self;
}
- (void)loadData{
NSArray *temArray =
@[
@{@"name":@"HK",@"imageUrl":@"http://HK",@"num":@"99"},
@{@"name":@"KD",@"imageUrl":@"http://KD",@"num":@"99"},
@{@"name":@"CC",@"imageUrl":@"http://CC",@"num":@"99"},
@{@"name":@"KC",@"imageUrl":@"http://KC",@"num":@"59"},
@{@"name":@"LA",@"imageUrl":@"http://LA",@"num":@"24"}];
for (int i = 0; i<temArray.count; i++) {
Model *m = [Model modelWithDictionary:temArray[i]];
[self.dataArray addObject:m];
}
}
#pragma mark -LGProtocol
- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath{
@synchronized (self) {
if (indexpath.row < self.dataArray.count) {
Model *model = self.dataArray[indexpath.row];
model.num = num;
}
}
}
#pragma mark - lazy
- (NSMutableArray *)dataArray{
if (!_dataArray) {
_dataArray = [NSMutableArray arrayWithCapacity:10];
}
return _dataArray;
}
@end
在MVCTableViewCell
中,删去UI
对Model
的数据绑定。在按钮点击的交互办法中,调用delegate
的didClickNum
办法
- (void)didClickAddBtn:(UIButton *)sender{
self.num++;
}
- (void)setNum:(int)num{
_num = num;
self.numLabel.text = [NSString stringWithFormat:@"%d",self.num];
// 发出响应 model delegate UI
if (self.delegate && [self.delegate respondsToSelector:@selector(didClickNum:indexpath:)]) {
[self.delegate didClickNum:self.numLabel.text indexpath:self.indexPath];
}
}
// 删去UI对Model的数据绑定
//- (void)setModel:(Model *)model{
// _model = model;
// self.numLabel.text = model.num;
// self.nameLabel.text = model.name;
//}
来到VC
代码中LMDataSource
的Block
,完结Cell
中UI
对Model
的数据绑定,以及delegate
和indexPath
的设置
#import "MVCViewController.h"
#import "LMDataSource.h"
#import "MVCTableViewCell.h"
#import "Present.h"
#import "Model.h"
#import "LGView.h"
@interface MVCViewController ()
@property (nonatomic, strong) LGView *lgView;
@property (nonatomic, strong) LMDataSource *dataSource;
@property (nonatomic, strong) Present *pt;
@end
@implementation MVCViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 树立联系
self.view = self.lgView;
[self.dataSource setDataArray:self.pt.dataArray];
[self.lgView setDataSource:self.dataSource];
}
#pragma mark - lazy
// UI布局代码
- (LGView *)lgView{
if(!_lgView){
_lgView = [[LGView alloc] initWithFrame:self.view.bounds];
}
return _lgView;
}
// 数据供给层
- (Present *)pt{
if(!_pt){
_pt = [[Present alloc] init];
}
return _pt;
}
// 数据署理层
- (LMDataSource *)dataSource{
if(!_dataSource){
__weak typeof(self) weakSelf = self;
_dataSource = [[LMDataSource alloc] initWithIdentifier:[MVCTableViewCell reuserId] configureBlock:^(MVCTableViewCell *cell, Model *model, NSIndexPath *indexPath) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
cell.numLabel.text = model.num;
cell.nameLabel.text = model.name;
cell.delegate = strongSelf.pt;
cell.indexPath = indexPath;
}];
}
return _dataSource;
}
@end
双向绑定
开发中遇到以下需求时,需要UI和Model进行双向绑定。例如:触发Cell
的didClickAddBtn
事情,当num++
大于5
,UI
层只展示前两条数据
修正LGProtocol
,增加UI
改写的事情接口
#import <Foundation/Foundation.h>
@protocol LGProtocol <NSObject>
- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath;
- (void)reloadUI;
@end
修正Present
中的didClickNum
办法,增加num
大于5
,只保留前两条数据,并调用delegate
的reloadUI
办法改写UI
- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath{
@synchronized (self) {
if (indexpath.row < self.dataArray.count) {
Model *model = self.dataArray[indexpath.row];
model.num = num;
if ([num intValue] > 5) {
[self.dataArray removeAllObjects];
NSArray *temArray =
@[ @{@"name":@"HK",@"imageUrl":@"http://HK",@"num":@"99"}, @{@"name":@"KC",@"imageUrl":@"http://KC",@"num":@"99"}];
for (int i = 0; i<temArray.count; i++) {
Model *m = [Model modelWithDictionary:temArray[i]];
[self.dataArray addObject:m];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(reloadUI)]) {
[self.delegate reloadUI];
}
}
}
}
}
修正LGView
,完结LGProtocol
协议的改写UI
办法
#import <UIKit/UIKit.h>
#import "MVCTableViewCell.h"
#import "LGProtocol.h"
@interface LGView : UIView<LGProtocol>
@property (nonatomic, strong) UITableView *tableView;
- (void)setDataSource:(id<UITableViewDataSource>)dataSource;
@end
@implementation LGView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.tableView];
}
return self;
}
- (void)setDataSource:(id<UITableViewDataSource>)dataSource{
self.tableView.dataSource = dataSource;
}
- (void)reloadUI{
[self.tableView reloadData];
}
- (UITableView *)tableView{
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:self.frame style:UITableViewStylePlain];
_tableView.tableFooterView = [UIView new];
_tableView.backgroundColor = [UIColor whiteColor];
[_tableView registerClass:[MVCTableViewCell class] forCellReuseIdentifier:[MVCTableViewCell reuserId]];
}
return _tableView;
}
@end
修正MVCViewController
,将UI
和Model
树立联系
- (void)viewDidLoad {
[super viewDidLoad];
// 树立联系
self.view = self.lgView;
[self.dataSource setDataArray:self.pt.dataArray];
[self.lgView setDataSource:self.dataSource];
//将UI和Model树立联系
self.pt.delegate = self.lgView;
}
关于上述MVP
的简略案例,其实还存在许多缺陷。这种粗陋的代码,连开发中常见的需求都难以满意
例如以下一些情况:
- 遇到更复杂的
Cell
形态 - 运用过多的
delegate
等胶水代码 - 当遇到模块层次更深的情况,相互之间难以通讯
所以,针对上述的问题,还要介绍一下MVP
的真实运用方式。