关于MVVM

来源:互联网 发布:网络电视台招聘 编辑:程序博客网 时间:2024/05/29 15:11

MVC
MVC 是 iOS 开发中使用最普遍的架构模式,同时也是苹果官方推荐的架构模式。MVC 代表的是 Model–view–controller 。
是的,MVC 看上去棒极了,model 代表数据,view 代表 UI ,而 controller 则负责协调它们两者之间的关系。然而,尽管从技术上看 view 和 controller 是相互独立的,但事实上它们几乎总是结对出现,一个 view 只能与一个 controller 进行匹配,反之亦然。
因此,M-VC 可能是对 iOS 中的 MVC 模式更为准确的解读。在一个典型的 MVC 应用中,controller 由于承载了过多的逻辑,往往会变得臃肿不堪,所以 MVC 也经常被人调侃成 Massive View Controller :
坦白说,有一部分逻辑确实是属于 controller 的,但是也有一部分逻辑是不应该被放置在 controller 中的。比如,将 model 中的 NSDate 转换成 view 可以展示的 NSString 等。在 MVVM 中,我们将这些逻辑统称为展示逻辑。
MVVM
因此,一种可以很好地解决 Massive View Controller 问题的办法就是将 controller 中的展示逻辑抽取出来,放置到一个专门的地方,而这个地方就是 viewModel 。
view :由 MVC 中的 view 和 controller 组成,负责 UI 的展示,绑定 viewModel 中的属性,触发 viewModel 中的命令;
viewModel :从 MVC 的 controller 中抽取出来的展示逻辑,负责从 model 中获取 view 所需的数据,转换成 view 可以展示的数据,并暴露公开的属性和命令供 view 进行绑定;
model :与 MVC 中的 model 一致,包括数据模型、访问数据库的操作和网络请求等。
小结
综上所述,我们只要将 MVC 中的 controller 中的展示逻辑抽取出来,放置到 viewModel 中,然后通过一定的技术手段,比如 RAC 来同步 view 和 viewModel ,就完成了 MVC 到 MVVM 的转变。
下面,我们直接上代码,一起来看一个 MVC 模式转换成 MVVM 模式的示例。首先是 model 层的代码 Person :

@interface Person : NSObject- (instancetype)initwithSalutation:(NSString *)salutation firstName:(NSString *)firstName lastName:(NSString *)lastName birthdate:(NSDate *)birthdate;@property (nonatomic, copy, readonly) NSString *salutation;@property (nonatomic, copy, readonly) NSString *firstName;@property (nonatomic, copy, readonly) NSString *lastName;@property (nonatomic, copy, readonly) NSDate *birthdate;@end

然后是 view 层的代码 PersonViewController ,在 viewDidLoad 方法中,我们将 Person 中的属性进行一定的转换后,赋值给相应的 view 进行展示:

- (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];}

接下来,我们引入一个 viewModel ,将 PersonViewController 中的展示逻辑抽取到这个 PersonViewModel 中:

文/你以为的只是你以为(简书作者)原文链接:http://www.jianshu.com/p/266b75e6126d著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。@interface PersonViewModel : NSObject- (instancetype)initWithPerson:(Person *)person;@property (nonatomic, strong, readonly) Person *person;@property (nonatomic, copy, readonly) NSString *nameText;@property (nonatomic, copy, readonly) NSString *birthdateText;@end@implementation PersonViewModel- (instancetype)initWithPerson:(Person *)person {self = [super init];if (self) {_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

最终,PersonViewController 将会变得非常轻量级:

- (void)viewDidLoad {[super viewDidLoad];self.nameLabel.text = self.viewModel.nameText;self.birthdateLabel.text = self.viewModel.birthdateText;}

怎么样?其实 MVVM 并没有想像中的那么难吧,而且更重要的是它也没有破坏 MVC 的现有结构,只不过是移动了一些代码,仅此而已。好了,说了这么多,那 MVVM 相比 MVC 到底有哪些好处呢?我想,主要可以归纳为以下三点:
由于展示逻辑被抽取到了 viewModel 中,所以 view 中的代码将会变得非常轻量级;
由于 viewModel 中的代码是与 UI 无关的,所以它具有良好的可测试性;
对于一个封装了大量业务逻辑的 model 来说,改变它可能会比较困难,并且存在一定的风险。在这种场景下,viewModel 可以作为 model 的适配器使用,从而避免对 model 进行较大的改动。
通过前面的示例,我们对第一点已经有了一定的感触;至于第三点,可能对于一个复杂的大型应用来说,才会比较明显;下面,我们还是使用前面的示例,来直观地感受下第二点好处:

SpecBegin(Person)NSString *salutation = @"Dr.";NSString *firstName = @"first";NSString *lastName = @"last";NSDate *birthdate = [NSDate dateWithTimeIntervalSince1970:0];it (@"should use the salutation available. ", ^{Person *person = [[Person alloc] initWithSalutation:salutation firstName:firstName lastName:lastName birthdate:birthdate];PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];expect(viewModel.nameText).to.equal(@"Dr. first last");});it (@"should not use an unavailable salutation. ", ^{Person *person = [[Person alloc] initWithSalutation:nil firstName:firstName lastName:lastName birthdate:birthdate];PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];expect(viewModel.nameText).to.equal(@"first last");});it (@"should use the correct date format. ", ^{Person *person = [[Person alloc] initWithSalutation:nil firstName:firstName lastName:lastName birthdate:birthdate];PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];expect(viewModel.birthdateText).to.equal(@"Thursday January 1, 1970");});SpecEnd

对于 MVVM 来说,我们可以把 view 看作是 viewModel 的可视化形式,viewModel 提供了 view 所需的数据和命令。因此,viewModel 的可测试性可以帮助我们极大地提高应用的质量。
文章相关:MVVM With ReactiveCocoa
原文:雷纯锋2011

0 0
原创粉丝点击