MVP_MVVM设计模式分享

  • 面临开发的业务逻辑越来越错综复杂,MVC已经很大程度上不能完全满足我们的需求,于是就出现了MVVM、MVP等架构。其中,最广为人知的是MVVM,虽说上手没那么容易,但是出于它能为controller减压的优越性,被广泛使用。
  • 重量级视图控制器会让整个ViewController变得非常复杂且不可维护,让维护者接近崩溃却无从下手。(题外话:MVP实际用起来确实没有MVVM用起来效果明显。)

需要Demo工程的请点击获取

MVP


MVP_MVVM设计模式分享_第1张图片
MVP结构图.png

MVP模式是MVC的一个演化版本,MVP全称Model-View-Presenter

  • Model:主要提供数据的存储功能,一般都是用来封装网络获取的json数据的集合对象,Presenter通过调用Model进行对象交互。
  • View:View与MVC中的V又有一些小差别,MVP中的View可以是Viewcontroller、View等控件,Presenter通过向View传model数据进行交互。
  • Presenter:作为model和view的中间人,从model层获取数据之后传给view,使得View和model没有耦合,义务逻辑处理可以在此处处理

MVP模式目录结构

MVP_MVVM设计模式分享_第2张图片
MVP模式目录结构.png

说明:对比MVC目录结构,多了Presenter的目录,数据交互处理都在Presenter中。就我理解,MVC中的Controller像一个大家族的大管家,MVP中的Presenter像这个家族中某个人的私人秘书


MVP模式代码及分析

Controller结构代码分析

@protocol HomeViewProtocol 

///城市列表
- (void)onGetResponseCityListEntity:(NSArray*)homeEntity fail:(NSInteger) errorCode des:(NSString *)des;

///行情列表
- (void)onGetResponseCondtionListEntity:(NSArray*)condtionEntity fail:(NSInteger) errorCode des:(NSString *)des;

@end
@interface HomeViewController ()

@property (nonatomic,strong) HomePresenter *homePresenter;

@property (nonatomic,strong) HomeCondtionPresenter *homeOtherPresenter;

@property (nonatomic,strong) NSMutableArray *listArray;

@property (nonatomic,strong) UITableView *tableView;

@end

@implementation HomeViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    _listArray = [[NSMutableArray alloc]initWithArray:@[@[],@[]]];
    
    _tableView = ({
        UITableView *tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 64, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame)-64) style:UITableViewStyleGrouped];
        [tableView registerNib:[UINib nibWithNibName:@"HomeViewCell" bundle:nil] forCellReuseIdentifier:NSStringFromClass([self class])];
        tableView.delegate = self;
        tableView.dataSource = self;
        [self.view addSubview:tableView];
        tableView;
    });
    
    //初始化HomePresenter,然后绑定一下自己的视图,
    _homePresenter = [[HomePresenter alloc]initWithView:self];
    [_homePresenter getCityResponseList];
    
    _homeOtherPresenter = [[HomeCondtionPresenter alloc]initWithView:self];
    [_homeOtherPresenter getCondtionResponseList];
}

///遵循HomeViewProtocol
- (void)onGetResponseCityListEntity:(NSArray *)homeEntity fail:(NSInteger)errorCode des:(NSString *)des {
    if (homeEntity) {
        _listArray[0] = [NSMutableArray arrayWithArray:homeEntity];
        [_tableView reloadData];
    }
}

- (void)onGetResponseCondtionListEntity:(NSArray *)condtionEntity fail:(NSInteger)errorCode des:(NSString *)des {
    if (condtionEntity.count > 0) {
        _listArray[1] = [NSMutableArray arrayWithArray:condtionEntity];
        [_tableView reloadData];        
    }
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return _listArray.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return _listArray[section].count;
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0) {
        HomeViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([self class])];
        [cell setModel:_listArray[0][indexPath.row]];
        return cell;
    } else {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
        if (!cell) {
            cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"UITableViewCell"];
        }
        NSDictionary *parm = _listArray[1][indexPath.row];
        cell.textLabel.text = parm[@"StoreName"]?:@"";
        cell.detailTextLabel.text = parm[@"StoreAddr"]?:@"";
        return cell;
    }
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0) {
        return 44;
    } else {
        return 66;
    }
}

@end
  • 因为MVP是协议式编程模式,所有需要遵守HomeViewProtocol协议。实现了该协议,当然需要实现对应的协议方法(onGetResponseCityListEntity: fail: des)。
  • 因为MVP是协议式编程模式,所有也需要绑定对应的View或者ViewController,所有[xxxPreenseter initWithView:self]方法。

Presenter结构代码及分析


@interface HomePresenter : HttpPresenter>

///测试城市列表请求
- (void)getCityResponseList;

@end


@implementation HomePresenter

///测试城市列表请求
- (void)getCityResponseList {
    NSString *url = @"https://mobileapi.centanet.com/index/url";
    [self.httpClient get:url parameters:nil];
}

- (void)onSuccess:(id)responseObject fail:(id)clientInfo errCode:(NSInteger)errCode errInfo:(NSString *)errInfo {
    if ([_view respondsToSelector:@selector(onGetResponseCityListEntity:fail:des:)]) {
        if (responseObject) {
            HomeEntity * model = [HomeEntity yy_modelWithJSON:responseObject];
            [_view onGetResponseCityListEntity:model.Result fail:errCode des:errInfo];
        } else {
            [_view onGetResponseCityListEntity:@[] fail:errCode des:errInfo];
        }
    }
}

@end

代码说明

  • 因为HomePresenter是继承HttpPresenter的,所以当调用自身的get或者post请求的时候,需要实现(onSuccess: fail: errCode: errInfo)请求回调方法
  • “HttpPresenter>”此泛型写法,将需要实现的协议方法转移到HomePresenter绑定的V中,本例子中就是通过这样的方法,将处理好的数据回调到Controller中去了

MVVM

MVP_MVVM设计模式分享_第3张图片
MVVM结构模式.png

MVVM模式也是MVC的一个演化版本,全称Model-View-ViewModel

  • Model:数据模型,和MVP,MVC没区别
  • View:在和MVC 和 MVP 模式下,View就是用户界面
  • ViewModel:是一个公开公共属性和命令的抽象的view。取代了 MVC 模式的 controller,或 MVP 模式的任命者(presenter),MVVM 有一个驱动。在 viewmodel 中,这种驱动传达视图和数据绑定的通信。

MVVM模式目录结构

MVP_MVVM设计模式分享_第4张图片
MVVM模式目录结构.png

MVVM模式代码及分析

Controller代码及分析

@interface TemplateViewController ()

@property (nonatomic,strong) UITableView *tableView;
@property (nonatomic,strong) TemplateViewModel *templateViewModel;

@end

@implementation TemplateViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    _templateViewModel = [[TemplateViewModel alloc]init];
    
    _tableView = ({
        UITableView *tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 64, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame)-64) style:UITableViewStyleGrouped];
        [tableView registerNib:[UINib nibWithNibName:@"TemplateCell" bundle:nil] forCellReuseIdentifier:NSStringFromClass([TemplateCell class])];
        [tableView registerNib:[UINib nibWithNibName:@"StoreViewCell" bundle:nil] forCellReuseIdentifier:NSStringFromClass([StoreViewCell class])];
        tableView.delegate = self;
        tableView.dataSource = self;
        [self.view addSubview:tableView];
        tableView;
    });
    
    WS(weakSelf)
    [_templateViewModel setReturnBlock:^(NSURLSessionDataTask *task, id responseObject) {
        [weakSelf.tableView reloadData];
    } failureBlock:^(NSURLSessionDataTask *task, NSError *error) {
    }];
    
    [self addHttpOperation:[_templateViewModel getCityList]];
    [self addHttpOperation:[_templateViewModel getStoreList]];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [_templateViewModel tempNumberOfSection];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return [_templateViewModel tempNumberOfSection:section];
}

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0) {
        TemplateCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([TemplateCell class])];
        [cell setModel:[_templateViewModel tempIndexPathOfCity:indexPath.row]];
        return cell;
    } else {
        StoreViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([StoreViewCell class])];
        [cell setModel:[_templateViewModel tempIndexPathOfStore:indexPath.row]];
        return cell;
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0) {
        return 44;
    } else {
        return 66;
    }
}

@end

代码说明

  • _templateViewModel对象调用getCityList和getStoreList方法,获取服务器数据,并处理数据
  • _templateViewModel对象通过block回调,刷新View的数据
  • tableView刷新数据后,通过_templateViewModel获取对应的数据条数和每个cell显示的数据内容

ViewModel代码及分析(重点)

@interface TemplateViewModel : BaseViewModel

@property (nonatomic,strong) NSMutableArray *resultStoreList;

//获取城市列表数据
- (NSURLSessionDataTask*)getCityList;

//获取门店列表数据
- (NSURLSessionDataTask*)getStoreList;

//tableView的组数(默认设置了2个section)
- (NSInteger)tempNumberOfSection;

//tableView每组显示的条数
- (NSInteger)tempNumberOfRowInSection:(NSInteger)section;

//第一个section的cell对应的model
- (TemplateResultEntity*)tempIndexPathOfCity:(NSInteger)row;

//第二个section的cell对应的model
- (NSDictionary*)tempIndexPathOfStore:(NSInteger)row;

@end

@implementation TemplateViewModel

- (instancetype)init {
    if (self = [super init]) {
        _resultStoreList = [[NSMutableArray alloc]init];
    }
    return self;
}

//获取城市列表数据
- (NSURLSessionDataTask *)getCityList{
    WS(weakSelf)
    return [ServiceClientRequest requestForCityListParm:nil success:^(NSURLSessionDataTask *task, id responseObject) {
        TemplateEntity *model =[TemplateEntity yy_modelWithJSON:responseObject];
        [weakSelf.resultArray addObjectsFromArray:model.Result];
        if (weakSelf.successBlock) {
            weakSelf.successBlock(task,responseObject);
        }
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        if (weakSelf.failureBlock) {
            weakSelf.failureBlock(task,error);
        }
    }];
}

//获取门店列表数据
- (NSURLSessionDataTask *)getStoreList {
    WS(weakSelf)
    return [ServiceClientRequest requestForStoreParm:nil success:^(NSURLSessionDataTask *task, id responseObject) {
        NSArray *resultArray = responseObject[@"Result"];
        [weakSelf.resultStoreList addObjectsFromArray:resultArray];
        if (weakSelf.successBlock) {
            weakSelf.successBlock(task,responseObject);
        }
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        if (weakSelf.failureBlock) {
            weakSelf.failureBlock(task,error);
        }
    }];
}

- (NSInteger)tempNumberOfSection {
    return 2;
}

- (NSInteger)tempNumberOfRowInSection:(NSInteger)section{
    if (section == 0) {
        return self.resultArray.count;
    } else {
        return self.resultStoreList.count;
    }
}

- (TemplateResultEntity*)tempIndexPathOfCity:(NSInteger)row {
    return self.resultArray[row];
}

- (NSDictionary *)tempIndexPathOfStore:(NSInteger)row {
    return self.resultStoreList[row];
}

@end

代码说明

  • getCityList和getStoreList负责向远程服务器获取数据和映射成对应的数据结构模型
  • tempNumberOfSection返回对应tableView的组数
  • tempNumberOfRowInSection返回对应的tableView某个indexSection的row数组个数

当tableView触发刷新数据的时候,就会从这些方法中获取需要的数据,从而将数据处理和逻辑处理给转移到ViewModel上了

你可能感兴趣的:(MVP_MVVM设计模式分享)