MVC解重解耦到MVP
2019-04-05 本文已影响0人
架构师的一小步
MVP面向协议编程->
- 主要目的:解重管理器Controller层,解耦view和model之间相互依赖的关系
- 解决方案:添加中介者层,降低view和model之间的耦合性。运用了中介者模式
- 代码部分:通过在中介者层,通过协议代理进行实现view和model之间的事件处理
example:UI改变更新model,model改变更新UI
- UI改变更新model
//第一步:问自己目的:这里是UI需要更新model,让中介者去更新model
//所以:需要将中介者对象给予UI
__weak typeof(self) weakSelf = self;
//左边UI,右边中介者
cell.delegate = weakSelf.pt;
//第二步:UI层需要做的事
1.一个遵循协议的delegate
@property (nonatomic, weak) id<PresentDelegate> delegate;
2.一个消息发送(UI发生改变时候需要通知那个需要改变者)
- (void)setNum:(int)num{
_num = num;
self.numLabel.text = [NSString stringWithFormat:@"%d",self.num];
//这里是通知这个代理对象进行调用
if (self.delegate && [self.delegate respondsToSelector:@selector(didClickAddBtnWithNum:indexPath:)]) {
[self.delegate didClickAddBtnWithNum:self.numLabel.text indexPath:self.indexPath];
}
}
第三步:Model层需要做的是:由于这里是中介者模式所以由中介者处理model层
1.遵循这个协议
#import "PresentDelegate.h"
@interface Present : NSObject<PresentDelegate>
@property (nonatomic, weak) id<PresentDelegate> delegate;
2.实现改变model的方法
#pragma mark - PresentDelegate
- (void)didClickAddBtnWithNum:(NSString *)num indexPath:(NSIndexPath *)indexPath{
// @synchronized(self){
//
// }
for (int i = 0; i<self.dataArray.count; i++) {
// 查数据 ---> 钱
if (i == indexPath.row) {// 商品ID 容错
Model *m = self.dataArray[indexPath.row];
m.num = num;
break;
}
}
}
- model改变更新UI
第一步:明确目的是让UI层去更新界面:这里是中介者让ui层更新界面
// MVPViewController.m
//左边中介者,右边UI(这里是MVPViewController)
self.pt.delegate = self;
第二步:中介者层
- (void)didClickAddBtnWithNum:(NSString *)num indexPath:(NSIndexPath *)indexPath{
// @synchronized(self){
//
// }
for (int i = 0; i<self.dataArray.count; i++) {
// 查数据 ---> 钱
if (i == indexPath.row) {// 商品ID 容错
Model *m = self.dataArray[indexPath.row];
m.num = num;
break;
}
}
if ([num intValue] > 6) {
NSArray *temArray =
@[
@{@"name":@"CC",@"imageUrl":@"http://CC",@"num":@"9"},
@{@"name":@"Gavin",@"imageUrl":@"http://Gavin",@"num":@"9"},
@{@"name":@"Cooci",@"imageUrl":@"http://Cooci",@"num":@"9"}];
[self.dataArray removeAllObjects];
for (int i = 0; i<temArray.count; i++) {
Model *m = [Model modelWithDictionary:temArray[i]];
[self.dataArray addObject:m];
}
//这里通知UI层进行更新UI操作
if (self.delegate && [self.delegate respondsToSelector:@selector(reloadDataForUI)]) {
[self.delegate reloadDataForUI];
}
}
}
第三步:UI层
// MVPViewController.m
#pragma mark - PresentDelegate
- (void)reloadDataForUI{
[self.dataSource addDataArray:self.pt.dataArray];
[self.tableView reloadData];
}
中介者模式
这里引用的第三方组件
podfile:
pod 'YYKit', '~> 1.0.9'
pod 'Masonry', '~> 1.1.0'
- 角色一:抽象中介者
//
// PresentDelegate.h
// 003--MVP
//
// Created by 王宁 on 2019/4/5.
// Copyright © 2019年 Cooci. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol PresentDelegate <NSObject>
// UI ---> model
- (void)didClickAddBtnWithNum:(NSString *)num indexPath:(NSIndexPath *)indexPath;
// 刷新UI ---> tableView VC
- (void)reloadDataForUI;
- (void)starAnmating;
@end
NS_ASSUME_NONNULL_END
- 角色二:具体中介者
//
// Present.h
// 003--MVP
//
// Created by Cooci on 2018/4/1.
// Copyright © 2018年 Cooci. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Model.h"
#import <YYKit.h>
#import "PresentDelegate.h"
@interface Present : NSObject<PresentDelegate>
@property (nonatomic, strong) NSMutableArray *dataArray;
- (void)loadData;
@property (nonatomic, weak) id<PresentDelegate> delegate;
@end
//
// Present.m
// 003--MVP
//
// Created by Cooci on 2018/4/1.
// Copyright © 2018年 Cooci. All rights reserved.
//
#import "Present.h"
@implementation Present
- (instancetype)init{
if (self = [super init]) {
[self loadData]; // 请求合适?
}
return self;
}
- (void)loadData{
NSArray *temArray =
@[
@{@"name":@"CC",@"imageUrl":@"http://CC",@"num":@"9"},
@{@"name":@"Gavin",@"imageUrl":@"http://Gavin",@"num":@"9"},
@{@"name":@"Cooci",@"imageUrl":@"http://Cooci",@"num":@"9"},
@{@"name":@"Dean",@"imageUrl":@"http://Dean ",@"num":@"9"},
@{@"name":@"CC",@"imageUrl":@"http://CC",@"num":@"9"},
@{@"name":@"Gavin",@"imageUrl":@"http://Gavin",@"num":@"9"},
@{@"name":@"Cooci",@"imageUrl":@"http://Cooci",@"num":@"9"},
@{@"name":@"Dean",@"imageUrl":@"http://Dean ",@"num":@"9"}];
for (int i = 0; i<temArray.count; i++) {
Model *m = [Model modelWithDictionary:temArray[i]];
[self.dataArray addObject:m];
}
}
#pragma mark - PresentDelegate
- (void)didClickAddBtnWithNum:(NSString *)num indexPath:(NSIndexPath *)indexPath{
// @synchronized(self){
//
// }
for (int i = 0; i<self.dataArray.count; i++) {
// 查数据 ---> 钱
if (i == indexPath.row) {// 商品ID 容错
Model *m = self.dataArray[indexPath.row];
m.num = num;
break;
}
}
if ([num intValue] > 6) {
NSArray *temArray =
@[
@{@"name":@"CC",@"imageUrl":@"http://CC",@"num":@"9"},
@{@"name":@"Gavin",@"imageUrl":@"http://Gavin",@"num":@"9"},
@{@"name":@"Cooci",@"imageUrl":@"http://Cooci",@"num":@"9"}];
[self.dataArray removeAllObjects];
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(reloadDataForUI)]) {
[self.delegate reloadDataForUI];
}
}
}
#pragma mark - lazy
- (NSMutableArray *)dataArray{
if (!_dataArray) {
_dataArray = [NSMutableArray arrayWithCapacity:10];
}
return _dataArray;
}
@end
- model层
//
// Model.h
// MVPDemo
//
// Created by Cooci on 2018/3/31.
// Copyright © 2018年 Cooci. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Model : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *imageUrl;
@property (nonatomic, copy) NSString *num;
@end
//
// Model.m
// MVPDemo
//
// Created by Cooci on 2018/3/31.
// Copyright © 2018年 Cooci. All rights reserved.
//
#import "Model.h"
@implementation Model
@end
- 通用的tableviewsource层
//
// LMDataSource.h
// LMShopCart
//
// Created by Cooci on 2018/3/29.
// Copyright © 2018年 Cooci. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef void (^CellConfigureBefore)(id cell, id model, NSIndexPath * indexPath);
@interface LMDataSource : NSObject<UITableViewDataSource,UICollectionViewDataSource>
@property (nonatomic, strong) NSMutableArray *dataArray;;
//自定义
- (id)initWithIdentifier:(NSString *)identifier configureBlock:(CellConfigureBefore)before;
//sb
@property (nonatomic, strong) IBInspectable NSString *cellIdentifier;
@property (nonatomic, copy) CellConfigureBefore cellConfigureBefore;
- (void)addDataArray:(NSArray *)datas;
- (id)modelsAtIndexPath:(NSIndexPath *)indexPath;
@end
//
// LMDataSource.m
// LMShopCart
//
// Created by Cooci on 2018/3/29.
// Copyright © 2018年 Cooci. All rights reserved.
//
#import "LMDataSource.h"
@implementation LMDataSource
- (id)initWithIdentifier:(NSString *)identifier configureBlock:(CellConfigureBefore)before {
if(self = [super init]) {
_cellIdentifier = identifier;
_cellConfigureBefore = [before copy];
}
return self;
}
- (void)addDataArray:(NSArray *)datas{
if(!datas) return;
if (self.dataArray.count>0) {
[self.dataArray removeAllObjects];
}
[self.dataArray addObjectsFromArray:datas];
}
- (id)modelsAtIndexPath:(NSIndexPath *)indexPath {
return self.dataArray.count > indexPath.row ? self.dataArray[indexPath.row] : nil;
}
#pragma mark UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return !self.dataArray ? 0: self.dataArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier forIndexPath:indexPath];
id model = [self modelsAtIndexPath:indexPath];
if(self.cellConfigureBefore) {
self.cellConfigureBefore(cell, model,indexPath);
}
return cell;
}
#pragma mark UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return !self.dataArray ? 0: self.dataArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:self.cellIdentifier forIndexPath:indexPath];
id model = [self modelsAtIndexPath:indexPath];
if(self.cellConfigureBefore) {
self.cellConfigureBefore(cell, model,indexPath);
}
return cell;
}
- (NSMutableArray *)dataArray{
if (!_dataArray) {
_dataArray = [NSMutableArray arrayWithCapacity:5];
}
return _dataArray;
}
@end
- 角色三:具体同事
//
// MVPTableViewCell.h
// MVPDemo
//
// Created by Cooci on 2018/3/31.
// Copyright © 2018年 Cooci. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "Present.h"
#import "PresentDelegate.h"
@interface MVPTableViewCell : UITableViewCell
@property (nonatomic, strong) UIButton *subBtn;
@property (nonatomic, strong) UILabel *nameLabel;
@property (nonatomic, strong) UILabel *numLabel;
@property (nonatomic, strong) UIButton *addBtn;
@property (nonatomic, assign) int num;
@property (nonatomic, strong) NSIndexPath *indexPath;
@property (nonatomic, weak) id<PresentDelegate> delegate;
@end
//
// MVPTableViewCell.m
// MVPDemo
//
// Created by Cooci on 2018/3/31.
// Copyright © 2018年 Cooci. All rights reserved.
//
#import "MVPTableViewCell.h"
#import <Masonry.h>
@implementation MVPTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
if (self == [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self setupUI];
}
return self;
}
- (void)layoutSubviews{
[super layoutSubviews];
[self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.contentView);
make.left.mas_equalTo(20);
}];
[self.addBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.contentView);
make.right.mas_equalTo(-50);
make.size.mas_equalTo(CGSizeMake(30, 30));
}];
[self.numLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.contentView);
make.right.equalTo(self.addBtn.mas_left);
make.size.mas_equalTo(CGSizeMake(50, 30));
}];
[self.subBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.numLabel.mas_left);
make.size.centerY.equalTo(self.addBtn);
}];
}
- (void)setupUI{
[self.contentView addSubview:self.nameLabel];
[self.contentView addSubview:self.subBtn];
[self.contentView addSubview:self.numLabel];
[self.contentView addSubview:self.addBtn];
self.num = 0;
}
#pragma mark - Action
- (void)didClickSubBtn:(UIButton *)sender{
if ([self.numLabel.text intValue]<=0) {return;}
self.num--;
}
- (void)didClickAddBtn:(UIButton *)sender{
if ([self.numLabel.text intValue]>=200) {return;}
self.num++;
}
#pragma mark - setter
- (void)setNum:(int)num{
_num = num;
self.numLabel.text = [NSString stringWithFormat:@"%d",self.num];
if (self.delegate && [self.delegate respondsToSelector:@selector(didClickAddBtnWithNum:indexPath:)]) {
[self.delegate didClickAddBtnWithNum:self.numLabel.text indexPath:self.indexPath];
}
}
#pragma mark - LAZY
- (UILabel *)numLabel{
if (_numLabel == nil) {
_numLabel = [[UILabel alloc] init];
_numLabel.text = @"0";
_numLabel.textAlignment = NSTextAlignmentCenter;
_numLabel.font = [UIFont systemFontOfSize:20];
_numLabel.textColor = [UIColor redColor];
}
return _numLabel;
}
- (UILabel *)nameLabel{
if (_nameLabel == nil) {
_nameLabel = [[UILabel alloc] init];
_nameLabel.text = @"Cooci";
_nameLabel.textAlignment = NSTextAlignmentCenter;
_nameLabel.font = [UIFont systemFontOfSize:20];
_nameLabel.textColor = [UIColor orangeColor];
}
return _nameLabel;
}
- (UIButton *)subBtn{
if (_subBtn == nil) {
_subBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_subBtn setTitle:@" - " forState:UIControlStateNormal];
[_subBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_subBtn.titleLabel.font = [UIFont systemFontOfSize:20];
[_subBtn setBackgroundColor:[UIColor blueColor]];
[_subBtn addTarget:self action:@selector(didClickSubBtn:) forControlEvents:UIControlEventTouchUpInside];
_subBtn.layer.cornerRadius = 15;
_subBtn.layer.masksToBounds = YES;
_subBtn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
}
return _subBtn;
}
- (UIButton *)addBtn{
if (!_addBtn) {
_addBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_addBtn setTitle:@" + " forState:UIControlStateNormal];
[_addBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_addBtn.titleLabel.font = [UIFont systemFontOfSize:20];
[_addBtn setBackgroundColor:[UIColor blueColor]];
[_addBtn addTarget:self action:@selector(didClickAddBtn:) forControlEvents:UIControlEventTouchUpInside];
_addBtn.layer.cornerRadius = 15;
_addBtn.layer.masksToBounds = YES;
_addBtn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
}
return _addBtn;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
@end
//
// MVPViewController.h
// MVPDemo
//
// Created by Cooci on 2018/3/31.
// Copyright © 2018年 Cooci. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface MVPViewController : UIViewController
@end
//
// MVPViewController.m
// MVPDemo
//
// Created by Cooci on 2018/3/31.
// Copyright © 2018年 Cooci. All rights reserved.
//
#import "MVPViewController.h"
#import "LMDataSource.h"
#import "MVPTableViewCell.h"
#import "Model.h"
#import <YYKit.h>
#import "Present.h"
#import "PresentDelegate.h"
static NSString *const reuserId = @"reuserId";
@interface MVPViewController ()<PresentDelegate>
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) NSMutableArray *dataArray;
@property (nonatomic, strong) Present *pt;
@property (nonatomic, strong) LMDataSource *dataSource;
@end
/**
1:解重 : 根据 VC model view
1.1:View ?
1.2:cell复用 : 原因 ---> UI事件 (cellforrow) VS 数据源没有改变
UI<---->Model 双向绑定 ?
2:解耦
2.1 通讯 ---> MVP : 面向协议时编程 : 代理
*/
@implementation MVPViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 建立连接
self.pt = [[Present alloc] init];
__weak typeof(self) weakSelf = self;
self.dataSource = [[LMDataSource alloc] initWithIdentifier:reuserId configureBlock:^(MVPTableViewCell *cell, Model *model, NSIndexPath *indexPath) {
// 函数式编程
// RAC 编程思想之集大成者
cell.nameLabel.text = model.name;
cell.numLabel.text = model.num;
cell.indexPath = indexPath;
cell.delegate = weakSelf.pt;
}];
[self.dataSource addDataArray:self.pt.dataArray];
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.tableView];
self.tableView.dataSource = self.dataSource;
self.pt.delegate = self;
}
#pragma mark - PresentDelegate
- (void)reloadDataForUI{
[self.dataSource addDataArray:self.pt.dataArray];
[self.tableView reloadData];
}
#pragma mark - lazy
- (UITableView *)tableView{
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
_tableView.backgroundColor = [UIColor whiteColor];
[_tableView registerClass:[MVPTableViewCell class] forCellReuseIdentifier:reuserId];
}
return _tableView;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end