美文网首页开源库
MVC解重解耦到MVP

MVC解重解耦到MVP

作者: 架构师的一小步 | 来源:发表于2019-04-05 13:11 被阅读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

相关文章

  • MVC解重解耦到MVP

    MVP面向协议编程-> 主要目的:解重管理器Controller层,解耦view和model之间相互依赖的关系 解...

  • 框架模式总结

    MVC:学习简单但是解耦不够彻底 MVP:解耦更加彻底,学习起来也项目比较简单,但是代码相对比较繁琐 MVVM:代...

  • MVVM框架使用指南

    MVC:学习简单但是解耦不够彻底 MVP:解耦更加彻底,学习起来也相对比较简单,但是代码相对比较繁琐 MVVM:代...

  • 一句话概括MVX的区别

    MVC->MVP,通过P层将M和V层完全解耦MVP->MVVM,由事件驱动改为数据驱动MVC,最早的架构模式,只是...

  • Android Mvp-Rxjava-Retrofit最佳实践

    有考虑用封装好的mvp框架,但是考虑到扩展性,还是自己写的好改。 那就自己来吧。 mvp和mvc最大的不同在于解耦...

  • MVVM的简单使用

    我们知道在今年MVP的设计模式非常的火。MVP 是由 MVC 演变而来,在代码的解耦层次更加的独到!避免了更多的代...

  • Android Studio开发快速创建MVP框架插件Andro

    Android开发中,我们为了代码的解耦以及后期的维护方便,都会采用一些开发框架,常用的有MVC、MVP、MVVM...

  • 注解在项目中的使用

    很早之前写过一篇关于MVP模式的文章,大家都知道MVP模式相对于MVC来说更加的解耦,当然了现在还有比较热的MVV...

  • 解耦与 MVC

    解耦与 MVC 版权声明:本文为 cheng-zhi 原创文章,可以随意转载,但必须在明确位置注明出处! 什么是解...

  • Kotlin&mvp&模块化&dagge

    Kotlin就不说了..谷歌爸爸的亲儿子...MVP就不说了..解耦彻底..利于维护模块化就不说了..解耦更彻底....

网友评论

    本文标题:MVC解重解耦到MVP

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