美文网首页
架构模式

架构模式

作者: 浅墨入画 | 来源:发表于2021-11-08 19:32 被阅读0次

    架构设计-轻量化VC

    MVC架构
    MVC架构图

    我们在使用MVC架构的时候会有耦合度的问题,如下所示

    image.png

    以下案例来说明

    • 我们在使用TableView展示列表的时候,经常这样用
    image.png image.png
    • 为了防止滑动页面的时候,又会展示成初始化的数据源,我们就需要把页面与数据源绑定,点击页面按钮需要更新数据源
    image.png

    上面的代码可以在cell中更改数据源,违背了架构的原则,model层与view层产生了耦合。架构的原则是高内聚,低耦合,谁的事情谁做

    VC的意义
    VC的意义
    • 上面例子中的数据放入model层
    image.png
    @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添加代理响应
    image.png
    • 设置cell代理
    image.png

    架构设计-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

    适配器加载多类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
    

    架构设计-context设计

    架构设计-context分发

    相关文章

      网友评论

          本文标题:架构模式

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