架构模式
2021-11-08 本文已影响0人
浅墨入画
架构设计-轻量化VC
MVC架构
MVC架构图我们在使用MVC
架构的时候会有耦合度的问题,如下所示
以下案例来说明
- 我们在使用
TableView
展示列表的时候,经常这样用
- 为了防止滑动页面的时候,又会展示成初始化的数据源,我们就需要把
页面与数据源绑定
,点击页面按钮需要更新数据源
上面的代码可以在cell
中更改数据源,违背了架构的原则,model
层与view
层产生了耦合。架构的原则是高内聚,低耦合,谁的事情谁做
VC的意义
VC的意义- 上面例子中的数据
放入model层
@property (nonatomic, strong) Present *pt;
- (void)viewDidLoad {
[super viewDidLoad];
// 数据提供层,让model自带数据
self.pt = [[Present alloc] init];
}
- 拆分
dataSource
#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;
@property (nonatomic, strong) IBInspectable NSString *cellIdentifier;
@property (nonatomic, copy) CellConfigureBefore cellConfigureBefore;
- (void)addDataArray:(NSArray *)datas;
- (id)modelsAtIndexPath:(NSIndexPath *)indexPath;
@end
<!-- LMDataSource.m -->
#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
image.png
- 对于页面繁重的
UI
,我们可以自定义view,让self.view = self.customView
,把UI层抽出来,减少VC
过重。
架构设计-MVP构建
- 去掉上面
model
层与view
层产生的耦合
- (void)setNum:(int)num{
_num = num;
self.numLabel.text = [NSString stringWithFormat:@"%d",self.num];
// 去掉更改数据的操作
}
// viewDidLoad方法修改
self.dataSource = [[LMDataSource alloc] initWithIdentifier:reuserId configureBlock:^(MVCTableViewCell *cell, Model *model, NSIndexPath *indexPath) {
cell.numLabel.text = model.num;
cell.nameLabel.text = model.name;
}];
运行工程点击页面,更改页面数据
,滑动页面之后数据又变成初始化的数据。
这里推荐面向协议思想
即MVP
架构,解决上面model
层与UI
层的通信。
-
model层
添加协议,并实现协议方法
@protocol PresentDelegate <NSObject>
// 需求: UI num -> model
- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath;
@end
@interface Present : NSObject<PresentDelegate>
@property (nonatomic, strong) NSMutableArray *dataArray;
@property (nonatomic,weak) id<PresentDelegate> delegate;
@end
<!-- Present.m实现协议方法 -->
#pragma mark -PresentDelegate
- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath{
// 保证数据的安全性
@synchronized (self) {
if (indexpath.row<self.dataArray.count) {
Model *model = self.dataArray[indexpath.row];
model.num = num
}
}
}
- 给
cell
添加代理响应
- 设置
cell
代理
架构设计-MVP思想总结
现在我们又了新的需求,比如说点击页面更新数据
,当数据大于6时,数据源的更新又会导致页面重新布局
。
- 添加刷新UI的代理方法
<!-- Present.h -->
@protocol PresentDelegate <NSObject>
// 需求: UI num -> model
- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath;
- (void)reloadUI;
@end
<!-- Present.m -->
#pragma mark -PresentDelegate
- (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] > 6) {
[self.dataArray removeAllObjects];
NSArray *temArray =
@[
@{@"name":@"Hank",@"imageUrl":@"http://Hank",@"num":@"99"},
@{@"name":@"Kody",@"imageUrl":@"http://Kody",@"num":@"99"}];
for (int i = 0; i<temArray.count; i++) {
Model *m = [Model modelWithDictionary:temArray[i]];
[self.dataArray addObject:m];
}
// model - delegate -> UI
if (self.delegate && [self.delegate respondsToSelector:@selector(reloadUI)]) {
[self.delegate reloadUI];
}
}
}
}
}
-
MVCViewController
遵守协议,并实现协议方法
@interface MVCViewController ()<PresentDelegate>
#pragma mark -PresentDelegate
- (void)reloadUI{
[self.dataSource addDataArray:self.pt.dataArray];
[self.tableView reloadData];
}
- 给
Present
设置代理
- (void)viewDidLoad {
self.pt.delegate = self;
}
小结
上面我们的流程是UI更新
-> 数据更新
-> UI更新
,实现了双向通信
。即双向绑定
MVP
思想步骤:根据需求
-> 接口设计
-> 功能实现
。MVVM
更多的是面向block,如果block嵌套层次过深
,会很难定位问题,而且数据安全会存在很大问题,维护成本会很高。面向协议
开发是点对点,就能避免层级过深
的问题。
架构设计-适配器设计
上面的案例虽然使用了MVP
设计模式,但是页面比较简单只有一个类型的cell
,如果有多个类型的cell
,该怎么设计呢?
使用适配器设计
加载不同类型cell
<!-- KCHomeAdapter.h -->
#import "KCBaseAdapter.h"
NS_ASSUME_NONNULL_BEGIN
@interface KCHomeAdapter : KCBaseAdapter
@end
NS_ASSUME_NONNULL_END
<!-- KCHomeAdapter.m -->
#import "KCHomeAdapter.h"
#import "KCHomeTableViewCell.h"
#import "KCChannelProfile.h"
@implementation KCHomeAdapter
- (CGFloat)getCellHeight:(NSInteger)row
{
CGFloat height = SCREEN_WIDTH*608/1080 + 54;
KCChannelProfile *model = [self.getAdapterArray objectAtIndex:row];
if (model.title.length > 0) {
CGSize titleSize = [model.title sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:14]}];
if (titleSize.width > SCREEN_WIDTH - 35) {
// 两行
height +=67;
}else{
height +=50;
}
}else{
height += 8;
}
return height;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self getCellHeight:indexPath.row];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.getAdapterArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// 二次封装 网络数据回来之后
// sucess
// model
// 定制化处理
KCChannelProfile* liveModel = self.getAdapterArray[indexPath.row];
UITableViewCell *cell = nil;
// 适配下发
CCSuppressPerformSelectorLeakWarning (
cell = [self performSelector:NSSelectorFromString([NSString stringWithFormat:@"tableView:cellForKCChannelProfile:"]) withObject:tableView withObject:liveModel];
);
return cell;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForKCChannelProfile:(id)model {
NSString *cellIdentifier = NSStringFromSelector(_cmd);
KCHomeTableViewCell *cell = (KCHomeTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[KCHomeTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
KCChannelProfile* liveModel = model;
[cell setCellContent:liveModel];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
id model = self.getAdapterArray[indexPath.row];
if (self.adapterDelegate && [self.adapterDelegate respondsToSelector:@selector(didSelectCellData:)]) {
[self.adapterDelegate didSelectCellData:model];
}
}
@end