对于 MVVM 我想说:自已的事情自已做,控制器就简化了!

对于 MVVM 我想说:自已的事情自已做,控制器就简化了!

太阳火神的美丽人生 (http://blog.csdn.net/opengl_es)

本文遵循“署名-非商业用途-保持一致”创作公用协议

转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino否则,出自本博客的文章拒绝转载或再转载,谢谢合作。



先来几个彩蛋:

http://numbbbbb.gitbooks.io/-the-swift-programming-language-/

http://swiftist.org/topics/165#18

http://swiftist.org/topics/148

http://swiftist.org/topics/150

http://swiftist.org/topics/129

http://dev.swiftguide.cn

http://practicalswift.com/2014/06/14/the-swift-standard-library-list-of-built-in-functions/

http://objccn.io/issue-13-1/

http://www.teehanlax.com/blog/model-view-viewmodel-for-ios/

http://www.cnblogs.com/yangfaxian/tag/iOS%20单元测试%20Xcode5/

戳完了没?!

可别说我这不叫彩蛋,纯是扯蛋噢!

扯蛋,就扯蛋吧,本山大叔拎着一串鹌鹑蛋,记忆犹新。



正文开始


有幸阅读到这么好的文章,语言描述得如此劲道,汗颜,语文学得太差了,想表达得如此干净利落,确力不从心。

关于 MVVM 所要解决的问题,在2010年的时侯,在工作项目中,就已经实践摸索过,当时总结出一句话:

自已的事情自已做,控制器只做大老爷!


后来,发现控制器中,有两件事情:

1、操作视图们;

2、操作模型们;


第二点,和 MVVM 的作法一样;

第一点,也无非就是把很多操作封装到视图子类中去,由子类自已来完成自已的管理工作,控制器只需要调用。


不过这样,有一个麻烦事,就是需要建子类。

就像我们平时说话,想说就说了,也不用打草稿,还得讲场合;

但子类化后,就像是在会议上做报告一样,得打草稿,建子类,把操作内置,然后再调用,到会上拿着稿发言。


当多了的时侯,你就会觉得烦了,但这样做对于后续迭代分析以及维护,都好处多多。


就像 iOS 中的表视图,大滚轮这类东西一样,显示一行,也要弄那么多的代理方法,每次都那么的烦!


凡事都有利有弊;或者说,都有适用的范围,没有适用万事万物的不变法则,只有变,才是不变的法则。


以下引用了 MVVM 相关的介绍,标题即为原文链接,猛戳即入!


再补充一句,每一个应用项目,当然多做些分析了抽象之后,你会发现,需要做的事情并不多,但那需要同时完成这个抽象过程在软件编码和架构上的同步抽象才行,要不然,这种抽象只能当作不存在,还以平常视之。


--------------------------

MVVM 介绍

朱宏旭 10 Jun 2014

分享文章

我于 2011 年在 500px 找到自己的第一份 iOS 开发工作。虽然我已经在大学里做了好几年 iOS 外包开发,但这才是我的一个真正的 iOS 开发工作。我被作为唯一的 iOS 开发者被招聘去实现拥有漂亮设计的 iPad 应用。在短短七周里,我们就发布了 1.0 并持续迭代,添加了更多特性,但从本质上,代码库也变得更加复杂了。

有时我感觉就像我不知道在做什么。虽然我知道自己的设计模式——就像任何好的编程人员那样 —— 但我太接近我在做的产品以至于不能客观地衡量我的架构决策的有效性。当队伍中来了另外一位开发者时,我意识到我们陷入困境了。

从没听过 MVC ?有人称之为 Massive View Controller(重量级视图控制器),这就是我们那时候的感觉。我不打算介绍令人汗颜的细节,但说实在的,如果我不得不再次重来一次,我绝对会做出不同的决策。

我会修改一个关键架构,并将其带入我从那时起就在开发的各种应用,即使用一种叫做 Model-View-ViewModel 的架构替换 Model-View-Controller。

所以,MVVM 到底是什么?与其专注于说明 MVVM 的来历,不如让我们看一个典型的 iOS 是如何构建的,并从那里了解 MVVM:


我们看到的是一个典型的 MVC 设置。Model 呈现数据,View 呈现用户界面,而 View Controller 调节它两者之间的交互。Cool!

稍微考虑一下,虽然 View 和 View Controller 是技术上不同的组件,但它们几乎总是手牵手在一起,成对的。你什么时候看到一个 View 能够与不同 View Controller 配对?或者反过来?所以,为什么不正规化它们的连接呢?


这更准确地描述了你可能已经编写的 MVC 代码。但它并没有做太多事情来解决 iOS 应用中日益增长的重量级视图控制器的问题。在典型的 MVC 应用里,许多逻辑被放在 View Controller 里。它们中的一些确实属于 View Controller,但更多的是所谓的“表示逻辑(presentation logic)”,以 MVVM 属术语来说,就是那些将 Model 数据转换为 View 可以呈现的东西的事情,例如将一个 NSDate 转换为一个格式化过的 NSString

我们的图解里缺少某些东西,那些使我们可以把所有表示逻辑放进去的东西。我们打算将其称为 “View Model” —— 它位于 View/Controller 与 Model 之间:


看起好多了!这个图解准确地描述了什么是 MVVM:一个 MVC 的增强版,我们正式连接了视图和控制器,并将表示逻辑从 Controller 移出放到一个新的对象里,即 View Model。MVVM 听起来很复杂,但它本质上就是一个精心优化的 MVC 架构,而 MVC 你早已熟悉。

现在我们知道了什么是 MVVM,但为什么我们会想要去使用它呢?在 iOS 上使用 MVVM 的动机,对我来说,无论如何,就是它能减少 View Controller 的复杂性并使得表示逻辑更易于测试。通过一些例子,我们将看到它如何达到这些目标。

此处有三个重点是我希望你看完本文能带走的:

  • MVVM 可以兼容你当下使用的 MVC 架构。
  • MVVM 增加你的应用的可测试性。
  • MVVM 配合一个绑定机制效果最好。

如我们之前所见,MVVM 基本上就是 MVC 的改进版,所以很容易就能看到它如何被整合到现有使用典型 MVC 架构的应用中。让我们看一个简单的 Person Model 以及相应的 View Controller:

@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

Cool!现在我们假设我们有一个 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;

}

所以,如你所见,并没有对我们的 MVC 架构做太多改变。还是同样的代码,只不过移动了位置。它与 MVC 兼容,带来更轻量的 View Controllers

可测试,嗯?是怎样?好吧,View Controller 是出了名的难以测试,因为它们做了太多事情。在 MVVM 里,我们试着尽可能多的将代码移入 View Model 里。测试 View Controller 就变得容易多了,因为它们不再做一大堆事情,并且 View 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

如果我们没有将这个逻辑移入 View Model,我们将不得不实例化一个完整的 View Controller 以及伴随的 View,然后去比较我们 View 中 Lable 的值。这样做不只是会变成一个麻烦的间接层,而且它只代表了一个十分脆弱的测试。现在,我们可以按意愿自由地修改视图层级而不必担心破坏我们的单元测试。使用 MVVM 带来的对于测试的好处非常清晰,甚至从这个简单的例子来看也可见一斑,而在有更复杂的表示逻辑的情况下,这个好处会更加明显。

注意到在这个简单的例子中, Model 是不可变的,所以我们可以只在初始化的时候指定我们 View Model 的属性。对于可变 Model,我们还需要使用一些绑定机制,这样 View Model 就能在背后的 Model 改变时更新自身的属性。此外,一旦 View Model 上的 Model 发生改变,那 View 的属性也需要更新。Model 的改变应该级联向下通过 View Model 进入 View。

在 OS X 上,我们可以使用 Cocoa 绑定,但在 iOS 上我们并没有这样好的配置可用。我们想到了 KVO(Key-Value Observation),而且它确实做了很伟大的工作。然而,对于一个简单的绑定都需要很大的样板代码,更不用说有许多属性需要绑定了。作为替代,我个人喜欢使用 ReactiveCocoa,但 MVVM 并未强制我们使用 ReactiveCocoa。MVVM 是一个伟大的典范,它自身独立,只是在有一个良好的绑定框架时做得更好。

我们覆盖了不少内容:从普通的 MVC 派生出 MVVM,看它们是如何相兼容的范式,从一个可测试的例子观察 MVVM,并看到 MVVM 在有一个配对的绑定机制时工作得更好。如果你有兴趣学习更多关于 MVVM 的知识,你可以看看这篇博客,它用更多细节解释了 MVVM 的好处,或者这一篇关于我们如何在最近的项目里使用 MVVM 获得巨大的成功的文章。我同样还有一个经过完整测试,基于 MVVM 的应用,叫做 C-41 ,它是开源的。去看看吧,如果你有任何疑问,请告诉我


话题 #13 下的更多文章

原文 Introduction to MVVM

关于译者


朱宏旭

留着长发的NIX,用过几年Linux,目前做 iOS 开发。喜欢科幻小说和科幻电影,因为幻想和梦境能带给我异世界体验。偶尔半梦半醒之间会质疑人生的意义,不知活着的所谓。

https://twitter.com/nixzhu





你可能感兴趣的:(对于 MVVM 我想说:自已的事情自已做,控制器就简化了!)