iOSMVPアーキテクチャパターン

一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して9日目です。クリックしてイベントの詳細をご覧ください

MVPプロトコル指向プログラミング、すなわちModel View Presenter(モデルビューコーディネーター)。

特徴

  • タスクの償却:最も重要なタスクPresenterより少ない機能に分割ModelしますView
  • テスト容易性:機能的に単純なViewレイヤーにより、ビジネスロジックのテストが簡単になります
  • MVC使いやすさ:パターンを使用するよりも多くのコードがありますが、MVPパターンは非常に明確な構造を持っています

したがって、MVCとのMVP違いは、MVPニュートラルModelとの間にはView直接の通信がないことです。

デザインパターン

  • ModelCacheこのレイヤーは、データオブジェクトを作成するだけでなく、ネットワークリクエストとデータ操作も含める必要があります
  • Viewレイヤーはいくつかのカプセル化、再利用です
  • Presenterレイヤーには、ネットワークリクエストやデータの操作は含まれませんが、レイヤーとレイヤーをCache構築するための単なるブリッジです。ModelView

アドバンテージ

  • モデルはビューから完全に分離されています。モデルに影響を与えずにビューを変更してください
  • すべての相互作用が含まPresenterれているため、モデルをより効率的に使用できます
  • ロジックを入れるとPresenter、ユーザーインターフェイスから離れた場所で単体テストを行うことができます
  • ロジックを変更せずに、 1つPresenerを複数のビューに使用できます。Presenterビューは常にモデルよりも頻繁に変更されるため、この機能は非常に便利です。

UIModelデカップリングあり

中央のボタンクリックのイベントインターフェイスを作成LGProtocolして実装しますCell

#import <Foundation/Foundation.h>

@protocol LGProtocol <NSObject>

- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath;

@end
复制代码

、プロトコルPresentを実装するメソッドLGProtocoldidClickNum

#import <Foundation/Foundation.h>
#import "Model.h"
#import "LGProtocol.h"
#import <YYKit.h>

@interface Present : NSObject<LGProtocol>

@property (nonatomic, strong) NSMutableArray    *dataArray;
@property (nonatomic, weak) id<LGProtocol>       delegate;

@end

@implementation Present

- (instancetype)init{
    if (self = [super init]) {
        [self loadData];
    }
    return self;
}


- (void)loadData{
    
    NSArray *temArray =
    @[
      @{@"name":@"HK",@"imageUrl":@"http://HK",@"num":@"99"},
      @{@"name":@"KD",@"imageUrl":@"http://KD",@"num":@"99"},
      @{@"name":@"CC",@"imageUrl":@"http://CC",@"num":@"99"},
      @{@"name":@"KC",@"imageUrl":@"http://KC",@"num":@"59"},
      @{@"name":@"LA",@"imageUrl":@"http://LA",@"num":@"24"}];

    for (int i = 0; i<temArray.count; i++) {
        Model *m = [Model modelWithDictionary:temArray[i]];
        [self.dataArray addObject:m];
    }
}

#pragma mark -LGProtocol
- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath{
    
    @synchronized (self) {
        if (indexpath.row < self.dataArray.count) {
            
            Model *model = self.dataArray[indexpath.row];
            model.num    = num;
        }
    }
}

#pragma mark - lazy
- (NSMutableArray *)dataArray{
    if (!_dataArray) {
        _dataArray = [NSMutableArray arrayWithCapacity:10];
    }
    return _dataArray;
}

@end
复制代码

で、ペアデータバインディングをMVCTableViewCell削除します。ボタンクリックのインタラクティブな方法でUIModeldelegatedidClickNum

- (void)didClickAddBtn:(UIButton *)sender{
    self.num++;
}

- (void)setNum:(int)num{
    _num                = num;
    self.numLabel.text  = [NSString stringWithFormat:@"%d",self.num];

    // 发出响应 model delegate UI 
    if (self.delegate && [self.delegate respondsToSelector:@selector(didClickNum:indexpath:)]) {
        [self.delegate didClickNum:self.numLabel.text indexpath:self.indexPath];
    }
}

// 删除UI对Model的数据绑定
//- (void)setModel:(Model *)model{
//    _model              = model;
//    self.numLabel.text  = model.num;
//    self.nameLabel.text = model.name;
//}
复制代码

VCコードに来て、真ん中のペアのデータバインディング完了LMDataSourceBlockCellUIModeldelegateindexPath

#import "MVCViewController.h"
#import "LMDataSource.h"
#import "MVCTableViewCell.h"
#import "Present.h"
#import "Model.h"
#import "LGView.h"

@interface MVCViewController ()

@property (nonatomic, strong) LGView            *lgView;
@property (nonatomic, strong) LMDataSource      *dataSource;
@property (nonatomic, strong) Present           *pt;

@end

@implementation MVCViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 建立关系
    self.view = self.lgView;
    [self.dataSource setDataArray:self.pt.dataArray];
    [self.lgView setDataSource:self.dataSource];
}

#pragma mark - lazy
// UI布局代码
- (LGView *)lgView{
    
    if(!_lgView){
        _lgView = [[LGView alloc] initWithFrame:self.view.bounds];
    }
    
    return _lgView;
}

// 数据提供层
- (Present *)pt{
    
    if(!_pt){
        _pt = [[Present alloc] init];
    }
    
    return _pt;
}

// 数据代理层
- (LMDataSource *)dataSource{
    
    if(!_dataSource){
        __weak typeof(self) weakSelf = self;
        _dataSource = [[LMDataSource alloc] initWithIdentifier:[MVCTableViewCell reuserId] configureBlock:^(MVCTableViewCell *cell, Model *model, NSIndexPath *indexPath) {
            __strong __typeof(weakSelf)strongSelf = weakSelf;
            cell.numLabel.text  = model.num;
            cell.nameLabel.text = model.name;
            cell.delegate       = strongSelf.pt;
            cell.indexPath      = indexPath;
        }];
    }
    
    return _dataSource;
}

@end
复制代码

双方向バインディング

開発中に次の要件が発生した場合、UIとモデル間の双方向バインディングが必要です。例:トリガーされCelldidClickAddBtnイベント、num++より大きい場合5UIレイヤーは最初の2つのデータのみを表示します

変更、更新イベントインターフェイスLGProtocolの追加UI

#import <Foundation/Foundation.h>

@protocol LGProtocol <NSObject>

- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath;

- (void)reloadUI;

@end
复制代码

変更Presentのメソッドは、より大きくdidClickNum増加し、最初の2つのデータのみを保持し、メソッドを呼び出して更新しますnum5delegatereloadUIUI

- (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] > 5) {

                [self.dataArray removeAllObjects];

                NSArray *temArray =
                @[
                  @{@"name":@"HK",@"imageUrl":@"http://HK",@"num":@"99"},
                  @{@"name":@"KC",@"imageUrl":@"http://KC",@"num":@"99"}];

                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(reloadUI)]) {
                    [self.delegate reloadUI];
                }
            }
        }
    }
}
复制代码

プロトコルの更新メソッドを変更LGView、実装しますLGProtocolUI

#import <UIKit/UIKit.h>
#import "MVCTableViewCell.h"
#import "LGProtocol.h"

@interface LGView : UIView<LGProtocol>

@property (nonatomic, strong) UITableView       *tableView;

- (void)setDataSource:(id<UITableViewDataSource>)dataSource;

@end

@implementation LGView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        
        [self addSubview:self.tableView];
    }
    return self;
}

- (void)setDataSource:(id<UITableViewDataSource>)dataSource{
    self.tableView.dataSource = dataSource;
}

- (void)reloadUI{
    [self.tableView reloadData];
}

- (UITableView *)tableView{
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:self.frame style:UITableViewStylePlain];
        _tableView.tableFooterView = [UIView new];
        _tableView.backgroundColor = [UIColor whiteColor];
        [_tableView registerClass:[MVCTableViewCell class] forCellReuseIdentifier:[MVCTableViewCell reuserId]];
    }
    return _tableView;
}

@end
复制代码

変更MVCViewController、意志UIModel関係の構築

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 建立关系
    self.view = self.lgView;
    [self.dataSource setDataArray:self.pt.dataArray];
    [self.lgView setDataSource:self.dataSource];
    //将UI和Model建立关系
    self.pt.delegate = self.lgView;
}
复制代码

上記のMVP単純なケースでは、まだ多くの欠陥があります。この種の基本的なコードは、開発における一般的なニーズでさえ満たすのが困難です。

たとえば、次の状況です。

  • より複雑なCellフォームに遭遇する
  • あまりにも多くのアイソdelegateグルーコードを使用している
  • モジュールレベルが深くなると、相互の通信が困難になります。

したがって、上記の問題に対応するだけでなくMVP、実際の使用方法を紹介します。

おすすめ

転載: juejin.im/post/7086831020703645710