iOS——MVVM设计模式

一、典型的iOS构架——MVC

 

  在典型的MVC设置中,Model呈现数据,Vie呈现用户界面,而ViewController调节它两者之间的交互。

  虽然View和View Controller是技术上不同的组件,但他们总是手牵手在一起,成对的,View不能和不同的View Controller配对,反之亦然。View和View Controller之间的关系如下图所示:

  在典型的MVC应用里,许多逻辑被放在ViewController里。它们中的一些确实属于View Controller,但更多的是“表示逻辑”(presentation logic),就是那些将Model数据转换为View可以呈现的东西的事情,例如将一个NSData转换为一个格式化过的NSString。典型的MVC设计模式没有做太多的事情来解决iOS应用中日益增长的重量级视图控制器的问题。

二、MVC的增强版——MVVM  

  在MVC中增加一个View Model,把所有表示逻辑放进去,它位于Model和View Controller之间,如下图所示:

  在MVVM中,正式链接了视图(View)和控制器(View Controller),并将表示逻辑从ViewController移出放到一个新的对象里,即View Model。

  MVVM的优势:

    *能减少ViewController的复杂性,并使得表示逻辑更易于测试;

    *可以兼容MVC架构;

    *增加应用程序的可测试性;

    *配合一个绑定机制效果最好。

  MVVM基本上就是MVC的改进版,所以很容易整合到现有使用典型MVC架构的应用中。

  下面看一个简单的Person Model以及相应的View Controller:

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic,readonly) NSString *salutation;
@property (nonatomic,readonly) NSString *firstname;
@property (nonatomic,readonly) NSString *lastname;
@property (nonatomic,readonly) NSDate *birthDate;

-(instancetype)initWithSalutation:(NSString *)salutation firstname:(NSString *)firstname lastname:(NSString *)lastname birthDate:(NSDate *)birthDate;
@end

下面有一个PersonViewController,在ViewDidLoad里,只需要基于他的model属性设置一些Label即可:

-(void)viewDidLoad{
    [super viewDidLoad];
    if (self.moedl.salutation.length > 0) {
        self.nameLabel.text = [NSString stringWithFormat:@"%@.%@.%@",self.moedl.salutation,self.moedl.firstname,self.moedl.lastname];
    }
    else{
        self.nameLabel.text = [NSString stringWithFormat:@"%@.%@",self.moedl.firstname,self.moedl.lastname];
    }
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
    [dateFormatter setDateFormat:@"EEEE MMMM d,yyyy"];
    self.birthdateLabel.text = [dateFormatter stringFromDate:self.moedl.birthDate];
}

  

  这全都直接了当了,典型的MVC。现在来看看如何用一个ViewModel来增强它,创建一个PersonViewModel:

PersonViewModel.h

#import <Foundation/Foundation.h>
#import "Person.h"

@interface PersonViewModel : NSObject
@property (nonatomic,readonly) Person *person;
@property (nonatomic,readonly) NSString *nameText;
@property (nonatomic,readonly) NSString *birthdateText;
-(instancetype)initWithPerson:(Person *)person;
@end

PersonViewModel.m

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

  这样就将viewDidLoad中的表示逻辑加入viewModel里。此时,新的viewDidLoad就会非常轻量:

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

  因此,我们并没有对MVC架构做太多改变。还是同样的代码,只是移动了位置。它与MVC兼容,带来更轻量级的ViewControllers。

  在MVVM里,我们试着尽可能多的将代码移入View Model里。测试ViewController就变得容易多了,因为它们不再做一大堆事情,并且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中Label值。这样做不只是会变成一个麻烦的间接层,而且它只代表了一个十分脆弱的测试。现在,可以按自己的意愿自由地修改视图层级而不必担心破坏单元测试。使用MVVM对测试带来的好处是显而易见的。

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

  在OS X上,可以使用cocoa绑定,但在iOS上并没有这样好的配置可用。但是,KVO为在iOS上实现提供了可能。

时间: 2024-11-05 16:04:15

iOS——MVVM设计模式的相关文章

你真的了解iOS代理设计模式吗?

OS中消息传递方式 在iOS中有很多种消息传递方式,这里先简单介绍一下各种消息传递方式. 通知:在iOS中由通知中心进行消息接收和消息广播,是一种一对多的消息传递方式. 代理:是一种通用的设计模式,iOS中对代理支持的很好,由代理对象.委托者.协议三部分组成. block:iOS4.0中引入的一种回调方法,可以将回调处理代码直接写在block代码块中,看起来逻辑清晰代码整齐. target action:通过将对象传递到另一个类中,在另一个类中将该对象当做target的方式,来调用该对象方法,从

iOS 关于 设计模式 与网友讨论实录

关于 设计模式 与网友讨论实录 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. 其实本篇就是想记录一下自已的一句对自已的总结,可能以前都没有意识到吧: 本图片版权归网友 杭州-老虎 所有. iO

设计模式笔记之三:Android DataBinding库(MVVM设计模式)

本博客转自郭霖公众号:http://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650236908&idx=1&sn=9e53f42e18a81795ef0cfe6fe3959ec2&scene=24&srcid=0910cK3vXJpNzY0CO28i1Qhs#wechat_redirect 什么是MVVM 说到DataBinding,就有必要先提起MVVM设计模式.Model–View–ViewModel(MVV

iOS.常用设计模式.02.委托模式

WTDelegate #import <Foundation/Foundation.h> @protocol WTDelegate <NSObject> @required - (void)sleep; - (void)eat; - (void)work; @end WTPhilosopher.h #import <Foundation/Foundation.h> #import "WTDelegate.h" @interface WTPhiloso

iOS.常用设计模式.01.单例模式

使用单例模式的类: UIApplication UIAccelerometer NSUserDefaults NSNotificationCenter NSFileManager NSBundle等 Singleton.h #import <Foundation/Foundation.h> @interface Singleton : NSObject // 始终返回同一个Singleton的指针 + (Singleton *)sharedManager; @property (strong,

Android DataBinding库(MVVM设计模式)

说到 DataBinding,就有必要先提起 MVVM设计模式. Model–View–ViewModel(MVVM) 是一个软件架构设计模式,相比 MVVM,大家对 MVC 或 MVP 可能会更加熟悉. MVC:(VIew-Model-Controller) 早期将 View.Model.Controller 代码块进行划分,使得程序大部分分离,降低耦合. MVP:(VIew-Model-Presenter) 由于 MVC 中 View和Model之间的依赖太强,导致 Activity 中的代

iOS的设计模式-KVC

关于iOS的设计模式,相信大家肯定不会陌生了,其中最常见的是MVC,也就是模型-视图-控制器模式,也是我们经常用的,这个这里就不在叙述了,这里主要讲一讲KVC这种模式,若文中有纰漏,请广大博友出来指正~~ 聊聊设计模式 虽然设计模式时时刻刻存在我们的项目工程里,但是我们却很少去注意它们,只是不自觉的去运用. 在软件设计领域,设计模式是对通用问题的可复用的解决方案.设计模式是一系列帮你写出更可理解和复用代码的模板,设计模式帮你创建松耦合的代码以便你不需要费多大力就可以改变或者替换代码中的组件. 所

MVVM设计模式基础知识--INotifyPropertyChanged接口

在.NET平台上,数据绑定是一项令人十分愉快的技术.利用数据绑定能减少代码,简化控制逻辑. 通常,可以将某个对象的一个属性绑定到一个可视化的控件上,当属性值改变时,控件上的显示数据也随之发生变化.要实现这一功能,只需要为自定义对象实现 INotifyPropertyChanged 接口即可.此接口中定义了 PropertyChanged 事件,我们只需在属性值改变时触发该事件即可. INotifyPropertyChanged 接口是 WPF/Silverlight 开发中非常重要的接口, 它构

通过TodoList案例对比Vue.js的MVVM设计模式与JQuery的MVP设计模式

Vue MVVM设计模式: 在使用vue进行编程时,不会再涉及到DOM的操作,取而代之的是修改数据层,当把数据进行变更的时候,vue之中它的底层会自动的根据数据的不同帮助我们去重新渲染页面. 编码时不需要关注VM层是如何实现的,它是vue内置的,只需要关注M层(模型层)和V层(视图层). 使用MVVM设计模式进行编码时,编码的重点一部分在于视图层,一部分在于模型层. data:指M层. <div>模板标签:指V层. vue:自身实现VM层.vue会监听到数据变了会帮你改变视图层,vue也能监听