关于MVVM

Posted phyLearn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于MVVM相关的知识,希望对你有一定的参考价值。

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

以上是关于关于MVVM的主要内容,如果未能解决你的问题,请参考以下文章

Android 应用程序架构 - MVVM 还是 MVC?

WinRT 开发:在 MVVM 模式中,关于绑定的几处技巧

iOS架构模式--解密 MVC,MVP,MVVM以及VIPER架构

WPF中的MVVM模式简单实现

TypeScript(Angular)中的MVVM模式[关闭]

(IOS)关于MVVM见解与实践