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 是很简单的,能够把信息显示清楚就可以)
3 - MVP 与 MVC 有着一个重大的区别:在 MVP 中 V 并不直接使用 Model 它们之间的通信是通过 Presenter 来进行的,所有的交互都发生在 Presenter 内部。就算是 Model 和 View 之间存在数据交互,也仅仅提供最简单的 setter/getter
3 - MVP 的好坏
① 好处
可以编写测试用的 View 模拟用户的各种操作,从而实现对 Presenter 的测试,而再不需要使用自动化的测试工具
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
运行效果