iOS基础- 架构模式:MVP
MVP
1 – MVP 是 MVC 设计模式派生出来的,它经常用来创建用户界面
2 – MVP 工作原理
① MVP 中 Presenter 完全把 Model 和 View 进行了分离,主要的程序逻辑在 Presenter 里实现。模型与视图完全分离,我们可以修改视图而不影响模型
② Presenter 与 View 是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更 View 时可以保持 Presenter 的不变。实际开发中往往可以将一个 Presenter 用于多个 View,而不需要改变 Presenter 的逻辑。这个特性非常的有用,因为 View 的变化总是比 Model 的变化频繁
③ 所有的交互都发生在 Presenter 内部,可以更高效地使用 Model注:程序逻辑主要在 Presenter 里实现,其中的 Model 是很薄的一层(View 是很简单的,能够把信息显示清楚就可以)
2 – MVP 与 MVC 有着一个重大的区别:在 MVP 中 V 并不直接使用 Model 它们之间的通信是通过 Presenter 来进行的,所有的交互都发生在 Presenter 内部。就算是 Model 和 View 之间存在数据交互,也仅仅提供最简单的 setter/getter
3 – MVP 的好坏
① 好处
可以编写测试用的 View 模拟用户的各种操作,从而实现对 Presenter 的测试,而再不需要使用自动化的测试工具
可以在 Model 和 View 都没有完成时候就可以通过编写 Mock Object(实现了 Model 和 View 的接口,但没有具体内容)来测试 Presenter 的逻辑
注:就是说完全可以脱离用户接口来实现单元测试
② 坏处
由于对视图的渲染放在了 Presenter 中,所以视图和 Presenter 的交互会过于频繁
Model 亦是如此,MVP 模式容易造成接口类爆炸、类文件和接口文件过多的问题,从而增大包的体积
MVP 实战
1 – 思路:MVP 在 iOS 中的表现无非就是让 P 承担了 Controller 的功能,M、V 依旧保持不变。实现方法也很简单,我们利用属性只需把 P 和 C 绑定在一起即可(互为彼此:注意循环依赖问题)。目录结构如图
2 – 代码示例
// – TestView.h
1 #import <UIKit/UIKit.h> 2 // 制定协议( P 接受) 3 @protocol TestViewDelegate <NSObject> 4 5 -(void)doSomethings; 6 7 @end 8 9 @interface TestView : UIView 10 11 @property(nonatomic,strong)UILabel *TVLabel; 12 @property(nonatomic,weak)id<TestViewDelegate>delegate; // 代理 13 14 // 提供一个简单入口 15 -(void)setName:(NSString *)name withimage:(UIImage *)image; 16 17 18 @end
// – TestView.m
1 #import "TestView.h" 2 @implementation TestView 3 4 - (instancetype)initWithFrame:(CGRect)frame 5 { 6 self = [super initWithFrame:frame]; 7 if (self) { 8 9 self.TVLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 40, self.frame.size.width -40, self.frame.size.height -80)]; 10 self.TVLabel.backgroundColor = [UIColor orangeColor]; 11 self.TVLabel.textAlignment = NSTextAlignmentCenter; 12 [self addSubview:self.TVLabel]; 13 14 } 15 return self; 16 } 17 18 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ 19 20 if ([self.delegate respondsToSelector:@selector(doSomethings)]) { 21 [self.delegate doSomethings]; 22 } 23 } 24 25 -(void)setName:(NSString *)name withimage:(UIImage *)image{ 26 27 // 键入你要做的事情 28 29 } 30 31 @end
// – TestModel.h
1 #import <Foundation/Foundation.h> 2 @interface TestModel : NSObject 3 4 // 数据源 5 @property(nonatomic,copy)NSString *name; 6 7 @end
// – TestPresenter.h
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface TestPresenter : NSObject // 绑定 Controller:就是为了让 P 拿到 C 来承担 C 的功能 @property(nonatomic,weak)UIViewController *controller; // 入口方法(初始化) -(void)setUP; @end
// – TestPresenter.m
1 #import "TestPresenter.h" 2 #import "TestView.h" 3 #import "TestModel.h" 4 // Presenter 既然替代了 controller 的功能,那么同视图的交互,我们同样采用代理 5 @interface TestPresenter()<TestViewDelegate> // 接受来自视图的协议 6 7 @end 8 9 @implementation TestPresenter 10 11 // P 完全负责 V、M 的交互 12 -(void)setUP{ 13 NSLog(@"---- P、C 两者绑定成功 -----"); 14 15 // 加载视图 16 TestView *tView = [[TestView alloc] initWithFrame:CGRectMake(30, 80, SCREEN_WIDTH - 60, 200)]; 17 tView.backgroundColor = [UIColor redColor]; 18 [self.controller.view addSubview:tView]; 19 tView.delegate = self; 20 21 // 加载数据模型 22 TestModel *tModel = [TestModel new]; 23 tModel.name = @"QQ"; 24 25 // 赋值 26 tView.TVLabel.text = tModel.name; 27 // V 可向 M 提供简单入口 28 [tView setName:nil withimage:nil]; 29 30 } 31 32 33 // 代理 34 - (void)doSomethings{ 35 36 // 随机色 37 self.controller.view.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 38 green:arc4random()%255/255.0 39 blue:arc4random()%255/255.0 40 alpha:1.0]; 41 } 42 43 @end
// – UIViewController.m
1 #import "ViewController.h" 2 #import "TestPresenter.h" 3 @interface ViewController() 4 5 // 绑定 Presenter 6 @property(nonatomic,strong)TestPresenter *presenter; 7 @end 8 9 @implementation ViewController 10 11 - (void)viewDidLoad { 12 [super viewDidLoad]; 13 self.view.backgroundColor = [UIColor cyanColor]; 14 15 // Presenter 16 self.presenter = [TestPresenter new]; 17 self.presenter.controller = self; 18 19 // 入口方法:在 P 中处理原 C 负责的事 20 [self.presenter setUP]; 21 22 } 23 24 @end
运行效果