普通的
MVC
架构
造成iOS 应用中日益增长的重量级视图控制器的问题,许多逻辑被放在 View Controller 里。它们中的一些确实属于 View Controller,但更多的是所谓的“表示逻辑(presentation logic)”,以 MVVM 属术语来说,就是那些将 Model 数据转换为 View 可以呈现的东西的事情,例如将一个 NSDate 转换为一个格式化过的 NSString 。
MVVM
![]()
·MVVM 可以兼容你当下使用的 MVC 架构。
·MVVM 增加你的应用的可测试性。
·MVVM 配合一个绑定机制效果最好。
View Controller 是出了名的难以测试,因为它们做了太多事情。在 MVVM 里,我们试着尽可能多的将代码移入 View Model 里。测试 View Controller 就变得容易
MVC:例子
@interface Person : NSObject
- (instancetype)initwithSalutation:( NSString *)salutation firstName:( NSString *)firstName lastName:( NSString *)lastName birthdate:( NSDate *)birthdate;
@property ( nonatomic , readonly ) NSString *salutation;
@property ( nonatomic , readonly ) NSString *firstName;
@property ( nonatomic , readonly ) NSString *lastName;
@property ( nonatomic , readonly ) NSDate *birthdate;
@end
现在我们假设我们有一个 PersonViewController ,在 viewDidLoad 里,只需要基于它的 model 属性设置一些 Label 即可。
- ( void )viewDidLoad {
[ super viewDidLoad];
if ( self .model.salutation.length > 0 ) {
self .nameLabel.text = [ NSString stringWithFormat:@ "%@ %@ %@" , self .model.salutation , self .model.firstName , self .model.lastName ];
} else {
self .nameLabel.text = [ NSString stringWithFormat:@ "%@ %@" , self .model.firstName , self .model.lastName ];
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@ "EEEE MMMM d, yyyy" ];
self .birthdateLabel.text = [dateFormatter stringFromDate:model .birthdate ];
}
标准的 MVC。现在来看看我们如何用一个 View Model 来增强它。
@interface PersonViewModel : NSObject
- (instancetype)initWithPerson:(Person *)person;
@property ( nonatomic , readonly ) Person *person;
@property ( nonatomic , readonly ) NSString *nameText;
@property ( nonatomic , readonly ) NSString *birthdateText;
@end
我们的 View Model 的实现大概如下:
@implementation PersonViewModel
- (instancetype)initWithPerson:(Person *)person {
self = [ super init];
if (! self ) return nil ;
_person = person;
if (person .salutation.length > 0 ) {
_nameText = [ NSString stringWithFormat:@ "%@ %@ %@" , self .person.salutation , self .person.firstName , self .person.lastName ];
} else {
_nameText = [ NSString stringWithFormat:@ "%@ %@" , self .person.firstName , self .person.lastName ];
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@ "EEEE MMMM d, yyyy" ];
_birthdateText = [dateFormatter stringFromDate:person .birthdate ];
return self ;
}
@end
Cool!我们已经将 viewDidLoad 中的表示逻辑放入我们的 View Model 里了。此时,我们新的 viewDidLoad 就会非常轻量:
- ( void )viewDidLoad {
[ super viewDidLoad];
self .nameLabel.text = self .viewModel.nameText ;
self .birthdateLabel.text = self .viewModel.birthdateText ;
}
MVVM 带来的对于测试的好处非常清晰
绑定框架ReactiveCocoa。MVVM 是一个伟大的典范,它自身独立,只是在有一个良好的绑定框架时做得更好。
在说 ReactiveCocoa 之前,先要介绍一下 FRP ( Functional Reactive Programming ,响应式编程)在维基百科中有这样一个例子介绍:
在命令式编程环境中, a = b + c 表示将表达式的结果赋给 a ,而之后改变 b 或 c 的值不会影响 a 。但在响应式编程中, a 的值会随着 b 或 c 的更新而更新。
Excel 就是响应式编程的一个例子。单元格可以包含字面值或类似 ”=B1+C1″ 的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化 。
造成iOS 应用中日益增长的重量级视图控制器的问题,许多逻辑被放在 View Controller 里。它们中的一些确实属于 View Controller,但更多的是所谓的“表示逻辑(presentation logic)”,以 MVVM 属术语来说,就是那些将 Model 数据转换为 View 可以呈现的东西的事情,例如将一个 NSDate 转换为一个格式化过的 NSString 。
MVVM
·MVVM 可以兼容你当下使用的 MVC 架构。
·MVVM 增加你的应用的可测试性。
·MVVM 配合一个绑定机制效果最好。
View Controller 是出了名的难以测试,因为它们做了太多事情。在 MVVM 里,我们试着尽可能多的将代码移入 View Model 里。测试 View Controller 就变得容易
MVC:例子
@interface Person : NSObject
- (instancetype)initwithSalutation:( NSString *)salutation firstName:( NSString *)firstName lastName:( NSString *)lastName birthdate:( NSDate *)birthdate;
@property ( nonatomic , readonly ) NSString *salutation;
@property ( nonatomic , readonly ) NSString *firstName;
@property ( nonatomic , readonly ) NSString *lastName;
@property ( nonatomic , readonly ) NSDate *birthdate;
@end
现在我们假设我们有一个 PersonViewController ,在 viewDidLoad 里,只需要基于它的 model 属性设置一些 Label 即可。
- ( void )viewDidLoad {
[ super viewDidLoad];
if ( self .model.salutation.length > 0 ) {
self .nameLabel.text = [ NSString stringWithFormat:@ "%@ %@ %@" , self .model.salutation , self .model.firstName , self .model.lastName ];
} else {
self .nameLabel.text = [ NSString stringWithFormat:@ "%@ %@" , self .model.firstName , self .model.lastName ];
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@ "EEEE MMMM d, yyyy" ];
self .birthdateLabel.text = [dateFormatter stringFromDate:model .birthdate ];
}
标准的 MVC。现在来看看我们如何用一个 View Model 来增强它。
@interface PersonViewModel : NSObject
- (instancetype)initWithPerson:(Person *)person;
@property ( nonatomic , readonly ) Person *person;
@property ( nonatomic , readonly ) NSString *nameText;
@property ( nonatomic , readonly ) NSString *birthdateText;
@end
我们的 View Model 的实现大概如下:
@implementation PersonViewModel
- (instancetype)initWithPerson:(Person *)person {
self = [ super init];
if (! self ) return nil ;
_person = person;
if (person .salutation.length > 0 ) {
_nameText = [ NSString stringWithFormat:@ "%@ %@ %@" , self .person.salutation , self .person.firstName , self .person.lastName ];
} else {
_nameText = [ NSString stringWithFormat:@ "%@ %@" , self .person.firstName , self .person.lastName ];
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@ "EEEE MMMM d, yyyy" ];
_birthdateText = [dateFormatter stringFromDate:person .birthdate ];
return self ;
}
@end
Cool!我们已经将 viewDidLoad 中的表示逻辑放入我们的 View Model 里了。此时,我们新的 viewDidLoad 就会非常轻量:
- ( void )viewDidLoad {
[ super viewDidLoad];
self .nameLabel.text = self .viewModel.nameText ;
self .birthdateLabel.text = self .viewModel.birthdateText ;
}
MVVM 带来的对于测试的好处非常清晰
绑定框架ReactiveCocoa。MVVM 是一个伟大的典范,它自身独立,只是在有一个良好的绑定框架时做得更好。
在说 ReactiveCocoa 之前,先要介绍一下 FRP ( Functional Reactive Programming ,响应式编程)在维基百科中有这样一个例子介绍:
在命令式编程环境中, a = b + c 表示将表达式的结果赋给 a ,而之后改变 b 或 c 的值不会影响 a 。但在响应式编程中, a 的值会随着 b 或 c 的更新而更新。
Excel 就是响应式编程的一个例子。单元格可以包含字面值或类似 ”=B1+C1″ 的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化 。