MVC、MVP和MVVM的开发模式的讲解以及事例举例

简析MVC、MVP和MVVM的模式的说明

MVC


传统MVC

下图是传统结构MVC,可以看到这种结构是紧耦合的,不推荐使用。

苹果的MVC

如下图,这是苹果MVC架构的愿景,Controller作为View和Model的中介,这样它们就解耦了。

各层的职责如下所示:

Model: 数据层,负责数据的处理和获取的数据接口层。
View: 展示层(GUI),对于 iOS 来说所有以 UI 开头的类基本都属于这层。
Controller: 控制器层,它是 Model 和 View 之间的胶水或者说是中间人。一般来说,当用户对 View 有操作时它负责去修改相应 Model;当 Model 的值发生变化时它负责去更新对应 View。
如上图所示,M和View应该是完全隔离的,由C作为中间人来负责二者的交互,同时三者是完全独立分开的,这样可以保证M和V的可测试性和复用性。

MVC在iOS中的实现

由于Apple的规范,一个界面的呈现都需要构建一个viewcontroller,而每个viewcontroller都带有一个根view,这就导致C和V紧密耦合在一起构成了iOS里面的C层,这明显违背了MVC的初衷。实际结构如下图。

根据苹果对MVC架构规范的描述,原文戳这里,viewcontroller其实是view和controller的组合,目的就是为了提高开发效率,简化操作。但这样也会导致一个问题,就是View层的代码全堆到了VC,比如VC中构建View,View的显示逻辑处理等等。

MVC各层功能

controller层(VC)

生成view,然后组装view
响应View的事件和作为view的代理
调用model的数据获取接口,拿到返回数据,处理加工,渲染到view显示
处理view的生命周期
处理界面之间的跳转
model层

业务逻辑封装
提供数据接口给controller使用
数据持久化存储和读取
作为数据模型存储数据
view层

界面元素搭建,动画效果,数据展示,
接受用户操作并反馈视觉效果
关于Model层,这里有一段文章引用说明了真正的Model层实际不应该只有几个结构和属性,应该有的是数据的业务逻辑。

理解Model层:
首先要正确的理解MVC中的M是什么?他是数据模型吗?答案是NO。他的正确定义是业务模型。也就是你所有业务数据和业务实现逻辑都应该定义在M层里面,而且业务逻辑的实现和定义应该和具体的界面无关,也就是和视图以及控制之间没有任何的关系,它是可以独立存在的,您甚至可以将业务模型单独编译出一个静态库来提供给第三方或者其他系统使用。
在上面经典MVC图中也很清晰的描述了这一点: 控制负责调用模型,而模型则将处理结果发送通知给控制,控制再通知视图刷新。因此我们不能将M简单的理解为一个个干巴巴的只有属性而没有方法的数据模型。
其实这里面涉及到一个最基本的设计原则,那就是面向对象的基本设计原则:就是什么是类?类应该是一个个具有不同操作和不同属性的对象的抽象(类是属性和方法的集合)。 我想现在任何一个系统里面都没有出现过一堆只有数据而没有方法的数据模型的集合被定义为一个单独而抽象的模型层来供大家使用吧。 我们不能把一个保存数据模型的文件夹来当做一个层,这并不符合横向切分的规则。
Model层实现的正确姿势:
定义的M层中的代码应该和V层和C层完全无关的,也就是M层的对象是不需要依赖任何C层和V层的对象而独立存在的。整个框架的设计最优结构是V层不依赖C层而独立存在,M层不依赖C层和V层独立存在,C层负责关联二者,V层只负责展示,M层持有数据和业务的具体实现,而C层则处理事件响应以及业务的调用以及通知界面更新。三者之间一定要明确的定义为单向依赖,而不应该出现双向依赖
M层要完成对业务逻辑实现的封装,一般业务逻辑最多的是涉及到客户端和服务器之间的业务交互。M层里面要完成对使用的网络协议(HTTP, TCP,其他)、和服务器之间交互的数据格式(XML, JSON,其他)、本地缓存和数据库存储(COREDATA, SQLITE,其他)等所有业务细节的封装,而且这些东西都不能暴露给C层。所有供C层调用的都是M层里面一个个业务类所提供的成员方法来实现。也就是说C层是不需要知道也不应该知道和客户端和服务器通信所使用的任何协议,以及数据报文格式,以及存储方面的内容。这样的好处是客户端和服务器之间的通信协议,数据格式,以及本地存储的变更都不会影响任何的应用整体框架,因为提供给C层的接口不变,只需要升级和更新M层的代码就可以了。比如说我们想将网络请求库从ASI换成AFN就只要在M层变化就可以了,整个C层和V层的代码不变。


MVP


结构如下图。

MVC中没有对业务逻辑和业务展示进行区分,MVP就是针对这一点进行的优化,它将业务逻辑和业务展示做了一层隔离。M和V功能不变, 原来的C现在只负责布局, 而所有的业务逻辑全都转移到了P层。P层处理完了业务逻辑,如果要更改view的显示,那么可以通过回调来实现,这样可以减轻耦合,MVP从视图层中分离了行为(事件响应)和状态(属性,用于数据展示),它创建了一个视图的抽象,也就是presenter层,而视图就是P层的『渲染』结果。P层中包含所有的视图渲染需要的动态信息,包括视图的内容(text、color)、组件是否启用(enable),除此之外还会将一些方法暴露给视图用于某些事件的响应。

MVP通信过程

当视图接收到来自用户的事件时,会将事件转交给 Presenter 进行处理;
被动的视图实现presenter的代理,当需要更新视图时 Presenter回调代理来更新视图的内容,这样让presenter专注于业务逻辑,view专注于显示逻辑
Presenter 负责对模型进行操作和更新,在需要时取出其中存储的信息
当模型层改变时,可以将改变的信息发送给观察者 Presenter

 

MVVM



MVVM由三个部分组成,也就是 Model、View 和 ViewModel;其中视图模型(ViewModel)其实就是 MVP 模式中的P,在 MVVM 中叫做VM,架构图如下。

MVVM相对于MVP做的改进就是对VM/P和view做了双向的数据和命令绑定,利用Binder机制使得Model和View可以状态同步。

下面我们就事例进一步分析说明三者的区别:

MVC开发模式

Objective-C中使用MVC(Model-View-Controller)架构模式进行开发是一种非常常见的做法,尤其是在iOS应用开发中。下面是一个简单的示例,来说明如何在Objective-C中实现一个基本的MVC结构。

1. 模型 (Model)

模型通常包含应用的数据逻辑。例如,假设我们有一个简单的Person模型:

 
1// Person.h
2@interface Person : NSObject
3
4@property (nonatomic, strong) NSString *name;
5@property (nonatomic, assign) NSInteger age;
6
7- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
8
9@end
10
11// Person.m
12@implementation Person
13
14- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
15    self = [super init];
16    if (self) {
17        _name = name;
18        _age = age;
19    }
20    return self;
21}
22
23@end

2. 视图 (View)

视图通常是UI相关的类,比如UIViewUITableView等。这里我们创建一个简单的PersonView,它是一个UIView的子类,用于显示Person的信息:

 
1// PersonView.h
2@interface PersonView : UIView
3
4@property (nonatomic, strong) UILabel *nameLabel;
5@property (nonatomic, strong) UILabel *ageLabel;
6
7- (instancetype)initWithFrame:(CGRect)frame;
8
9@end
10
11// PersonView.m
12@implementation PersonView
13
14- (instancetype)initWithFrame:(CGRect)frame {
15    self = [super initWithFrame:frame];
16    if (self) {
17        self.backgroundColor = [UIColor whiteColor];
18        
19        self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 280, 50)];
20        self.nameLabel.textAlignment = NSTextAlignmentCenter;
21        [self addSubview:self.nameLabel];
22        
23        self.ageLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 70, 280, 50)];
24        self.ageLabel.textAlignment = NSTextAlignmentCenter;
25        [self addSubview:self.ageLabel];
26    }
27    return self;
28}
29
30@end

3. 控制器 (Controller)

控制器是连接模型和视图的关键部分,它处理业务逻辑,并且控制模型和视图之间的交互。以下是一个简单的PersonViewController示例:

 
1// PersonViewController.h
2#import <UIKit/UIKit.h>
3
4@interface PersonViewController : UIViewController
5
6@end
7
8// PersonViewController.m
9#import "PersonViewController.h"
10#import "PersonView.h"
11#import "Person.h"
12
13@interface PersonViewController ()
14
15@property (nonatomic, strong) Person *person;
16@property (nonatomic, strong) PersonView *personView;
17
18@end
19
20@implementation PersonViewController
21
22- (void)viewDidLoad {
23    [super viewDidLoad];
24    
25    // 创建并配置视图
26    self.personView = [[PersonView alloc] initWithFrame:self.view.bounds];
27    [self.view addSubview:self.personView];
28    
29    // 创建并配置模型
30    self.person = [[Person alloc] initWithName:@"John Doe" age:30];
31    
32    // 更新视图
33    self.personView.nameLabel.text = self.person.name;
34    self.personView.ageLabel.text = [NSString stringWithFormat:@"%ld", (long)self.person.age];
35}
36
37@end

总结

这个简单的例子展示了MVC架构的基本组成部分:

  • 模型 (Person) 负责存储数据和业务逻辑。
  • 视图 (PersonView) 负责显示数据。
  • 控制器 (PersonViewController) 负责协调模型和视图之间的交互。

在实际应用中,控制器可能会更复杂,可能需要处理用户输入、网络请求、数据存储等更多的任务。此外,为了进一步解耦,你可以在模型和视图之间加入代理或通知机制,使得视图可以直接从模型获取数据,而不需要通过控制器作为中介。

MVP开发模式

在Objective-C中使用MVP(Model-View-Presenter)架构模式进行开发可以帮助你更好地组织代码,提高代码的可维护性和可测试性。下面是一个简单的示例,来说明如何在Objective-C中实现一个基本的MVP结构。

1. 模型 (Model)

模型通常包含应用的数据逻辑。例如,假设我们有一个简单的Person模型:

 
1// Person.h
2@interface Person : NSObject
3
4@property (nonatomic, strong) NSString *name;
5@property (nonatomic, assign) NSInteger age;
6
7- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
8
9@end
10
11// Person.m
12@implementation Person
13
14- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
15    self = [super init];
16    if (self) {
17        _name = name;
18        _age = age;
19    }
20    return self;
21}
22
23@end

2. 视图 (View)

视图通常是UI相关的类,比如UIViewController。这里我们创建一个简单的PersonViewController,它是一个UIViewController的子类,用于显示Person的信息。我们需要定义一个协议来描述视图的行为:

 
1// PersonViewProtocol.h
2@protocol PersonViewProtocol <NSObject>
3
4- (void)showPersonWithName:(NSString *)name age:(NSInteger)age;
5
6@end

接下来,实现视图类:

 
1// PersonViewController.h
2#import <UIKit/UIKit.h>
3#import "PersonViewProtocol.h"
4
5@interface PersonViewController : UIViewController <PersonViewProtocol>
6
7@end
8
9// PersonViewController.m
10#import "PersonViewController.h"
11#import "PersonPresenter.h"
12
13@interface PersonViewController ()
14
15@property (nonatomic, weak) PersonPresenter *presenter;
16
17@end
18
19@implementation PersonViewController
20
21- (void)viewDidLoad {
22    [super viewDidLoad];
23    
24    // 初始化 presenter
25    self.presenter = [[PersonPresenter alloc] initWithView:self];
26    
27    // 假设这是从服务器获取数据的调用
28    [self.presenter fetchPersonData];
29}
30
31- (void)showPersonWithName:(NSString *)name age:(NSInteger)age {
32    // 在这里展示数据
33    NSLog(@"Displaying person with name: %@ and age: %ld", name, (long)age);
34}
35
36@end

3. Presenter (Presenter)

Presenter是连接模型和视图的关键部分,它处理业务逻辑,并且控制模型和视图之间的交互。以下是一个简单的PersonPresenter示例:

 
1// PersonPresenter.h
2#import <Foundation/Foundation.h>
3#import "PersonViewProtocol.h"
4
5@protocol PersonPresenterDelegate <NSObject>
6
7- (void)personPresenterDidLoadPerson:(Person *)person;
8
9@end
10
11@interface PersonPresenter : NSObject
12
13@property (nonatomic, weak) id<PersonViewProtocol> view;
14@property (nonatomic, weak) id<PersonPresenterDelegate> delegate;
15
16- (instancetype)initWithView:(id<PersonViewProtocol>)view;
17
18- (void)fetchPersonData;
19
20@end
21
22// PersonPresenter.m
23#import "PersonPresenter.h"
24#import "Person.h"
25
26@implementation PersonPresenter
27
28- (instancetype)initWithView:(id<PersonViewProtocol>)view {
29    self = [super init];
30    if (self) {
31        _view = view;
32    }
33    return self;
34}
35
36- (void)fetchPersonData {
37    // 模拟从网络加载数据
38    Person *person = [[Person alloc] initWithName:@"John Doe" age:30];
39    
40    if ([self.delegate respondsToSelector:@selector(personPresenterDidLoadPerson:)]) {
41        [self.delegate personPresenterDidLoadPerson:person];
42    }
43}
44
45@end

4. 使用Presenter

PersonViewController中,我们已经设置了Presenter,并在viewDidLoad方法中调用了fetchPersonData方法。当数据准备就绪后,Presenter会调用委托方法personPresenterDidLoadPerson:,然后视图会更新UI。

5. 实现委托

我们需要在PersonViewController中实现PersonPresenterDelegate协议,以便Presenter能够通知视图更新UI:

 
1// PersonViewController.h
2@interface PersonViewController : UIViewController <PersonViewProtocol, PersonPresenterDelegate>
3
4@end
5
6// PersonViewController.m
7@implementation PersonViewController
8
9- (void)personPresenterDidLoadPerson:(Person *)person {
10    [self showPersonWithName:person.name age:person.age];
11}
12
13@end

总结

这个简单的例子展示了MVP架构的基本组成部分:

  • 模型 (Person) 负责存储数据和业务逻辑。
  • 视图 (PersonViewController) 负责显示数据。
  • Presenter (PersonPresenter) 负责协调模型和视图之间的交互。

在实际应用中,Presenter可能会更复杂,可能需要处理用户输入、网络请求、数据存储等更多的任务。此外,为了进一步解耦,你可以考虑使用不同的协议和委托来实现更复杂的交互逻辑。

MVVM开发模式

Objective-C中使用MVVM(Model-View-ViewModel)架构模式进行开发是一种较为现代的方法,它可以帮助你更好地分离关注点,提高代码的可维护性和可测试性。下面是一个简单的示例,来说明如何在Objective-C中实现一个基本的MVVM结构。

1. 模型 (Model)

模型通常包含应用的数据逻辑。例如,假设我们有一个简单的Person模型:

 
1@interface Person : NSObject
2
3@property (nonatomic, strong) NSString *name;
4@property (nonatomic, assign) NSInteger age;
5
6@end
7
8@implementation Person
9
10- (instancetype)init {
11    self = [super init];
12    if (self) {
13        // 初始化属性
14    }
15    return self;
16}
17
18@end

2. 视图 (View)

视图通常是UI相关的类,比如UIViewController。这里我们创建一个简单的PersonViewController

 
1@interface PersonViewController : UIViewController
2
3@end
4
5@implementation PersonViewController
6
7- (void)viewDidLoad {
8    [super viewDidLoad];
9    // 设置界面
10}
11
12@end

3. 视图模型 (ViewModel)

视图模型是连接模型和视图的关键部分,它处理业务逻辑,并将数据转换成视图可以理解的形式。以下是一个简单的PersonViewModel示例:

 
1@interface PersonViewModel : NSObject
2
3@property (nonatomic, strong) Person *person;
4@property (nonatomic, copy) void (^updateView)(void);
5
6- (void)loadData;
7
8@end
9
10@implementation PersonViewModel
11
12- (instancetype)init {
13    self = [super init];
14    if (self) {
15        // 初始化属性
16    }
17    return self;
18}
19
20- (void)loadData {
21    // 模拟从网络加载数据
22    self.person = [[Person alloc] init];
23    self.person.name = @"John Doe";
24    self.person.age = 30;
25    
26    // 当数据准备好时调用更新视图的闭包
27    if (self.updateView) {
28        self.updateView();
29    }
30}
31
32@end

4. 视图与视图模型的交互

在视图控制器中,你需要实例化视图模型,并设置一个闭包来更新UI:

 
1#import "PersonViewController.h"
2#import "PersonViewModel.h"
3
4@interface PersonViewController ()
5
6@property (nonatomic, strong) PersonViewModel *viewModel;
7@property (nonatomic, strong) UILabel *nameLabel;
8@property (nonatomic, strong) UILabel *ageLabel;
9
10@end
11
12@implementation PersonViewController
13
14- (void)viewDidLoad {
15    [super viewDidLoad];
16    
17    self.viewModel = [[PersonViewModel alloc] init];
18    self.viewModel.updateView = ^{
19        [self updateUIWithModel:self.viewModel.person];
20    };
21    
22    [self.viewModel loadData];
23    
24    // 设置界面元素
25    self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, 280, 50)];
26    self.ageLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 150, 280, 50)];
27    
28    [self.view addSubview:self.nameLabel];
29    [self.view addSubview:self.ageLabel];
30}
31
32- (void)updateUIWithModel:(Person *)person {
33    dispatch_async(dispatch_get_main_queue(), ^{
34        self.nameLabel.text = person.name;
35        self.ageLabel.text = [NSString stringWithFormat:@"%ld", (long)person.age];
36    });
37}
38
39@end

以上就是一个简单的MVVM模式在Objective-C中的实现。在这个例子中,PersonViewController通过PersonViewModelPerson模型进行通信。PersonViewModel负责处理数据并通知视图更新。这种方式可以让你的代码更加模块化,易于理解和维护。

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值