MVVM

原文连接程序员

目前客户端最流行的架构应该就是MVVM,然而在看了一些文章以后发现大部分是理论而并无仔细讲解具体的架构方法和实践,这篇博客说说我在实际工做中的使用。算法

引言

提到MVVM咱们不得不先来认识一下MVC: MVC模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。MVC模式最先由Trygve Reenskaug在1978年提出[1],是施乐帕罗奥多研究中心(Xerox PARC)在20世纪80年代为程序语言Smalltalk发明的一种软件架构。MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,而且使程序某一部分的重复利用成为可能。除此以外,此模式经过对复杂度的简化,使程序结构更加直观。软件系统经过对自身基本部分分离的同时也赋予了各个基本部分应有的功能。专业人员能够经过自身的专长分组:数据库

  • 控制器(Controller)- 负责转发请求,对请求进行处理。
  • 视图(View) - 界面设计人员进行图形界面设计。
  • 模型(Model) - 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(能够实现具体的功能)。

MVVM


MVVM是Model-View-ViewModel的简写,最先是由微软公司提出并运用,是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构架构。MVVM有助于将图形用户界面的开发与业务逻辑或后端逻辑(数据模型)的开发分离开来,这是经过置标语言或GUI代码实现的。MVVM的视图模型是一个值转换器,这意味着视图模型负责从模型中暴露(转换)数据对象,以便轻松管理和呈现对象。在这方面,视图模型比视图作得更多,而且处理大部分视图的显示逻辑。视图模型能够实现中介者模式,组织对视图所支持的用例集的后端逻辑的访问。后端

  • 模型 模型是指表明真实状态内容的领域模型(面向对象),或指表明内容的数据访问层(以数据为中心)。
  • 视图 就像在MVC和MVP模式中同样,视图是用户在屏幕上看到的结构、布局和外观(UI)。
  • 视图模型 视图模型是暴露公共属性和命令的视图的抽象。MVVM没有MVC模式的控制器,也没有MVP模式的presenter,有的是一个绑定器。在视图模型中,绑定器在视图和数据绑定器之间进行通讯。
  • 绑定器 声明性数据和命令绑定隐含在MVVM模式中。在Microsoft解决方案堆中,绑定器是一种名为XAML的标记语言。绑定器使开发人员免于被迫编写样板式逻辑来同步视图模型和视图。在微软的堆以外实现时,声明性数据绑定技术的出现是实现该模式的一个关键因素。

MVVM的优势

解决controller过于臃肿


在MVC中很容易就会把一些业务逻辑,网络请求,数据IO都放在controller中安全

注意,这里不是说MVC的控制器必定很臃肿,而是「容易变得臃肿」网络

在咱们新建一个工程的时候,苹果会自动帮咱们生成一个ViewController,而在动手开始写代码的时候,每每控制不住就直接将逻辑写在Controller中。架构

MVVM架构会要求咱们把任何与非View的逻辑玻璃出来,Controller中除了绑定viewModel以外的代码只容许出现对View的操做。由于Controller对咱们来讲也只是一个View。框架

逻辑分离


就像上面说的,业务逻辑都会抽离出来放在viewModel中 ,这样能够在任何地方重用这一堆业务数据库设计

除此以外,咱们的代码将会更加易于测试,避免出如今MVC中可能出现的那种超长的方法、严重依赖全局状态致使难以测试的问题。mvvm

View重用 (可拥有view单独的viewModel)


在MVVM中,View只须要与ViewMode交互,不会收到其余的影响,因此不但vm、m 能够重用,view同样能够重复使用,修改的时候也更加方便。

缺点


BUG与传递


因为在MVVM里面View和ViewModel是松耦合的,在测试出问题的时候就要排查各个地方的问题,

有多是vm中的也有多是view中的。因为vm会传递数据,一个bug会很容易的传递到其余地方,引起更大的问题。

而且其中一个地方出现问题的话,这个BUG就极有可能随着传递到其余的逻辑中,从而致使更严重的问题发生。

须要维护Model,viewModel,和view的开销,controller至关于被抽象成View。


额外的viewModel使用也并非无代价的,有可能因为各类缘由致使管理起来稍微复杂。而额外的,若是由于强引用或其余缘由致使的循环引用等内存不能正确释放的状况下,有可能会内存疯涨,因此须要确保你的使用方式是无反作用的。

使用方法


上面说了这些只是一个大体的介绍,咱们仍是来看看应该怎样使用吧。

你可使用delegate的方式或者block的方式对view和viewModel进行桥接,在这里咱们选择使用delegate,我认为这样看着比较直观,在代码中也更加明确。

首先建立一个工程,选择singleViewApplication,咱们就以最多见的的登录功能做为示例

新建用于由viewModel调用,view进行响应的protocol


首先要起个名字,就叫LoginViewModelDelegateProtocol 吧

@protocol LoginViewModelDelegateProtocol <NSObject>

@end
复制代码

好,让咱们想想view会发送一些什么数据给VM ,VM都须要什么数据。

对于简单登录的VM来讲,咱们须要通知view的数据和方法

  • 登录成功
  • 错误提示
  • 按钮状态改变(是否能够点击)

那么咱们能够在protocol中添加方法了

@protocol LoginViewModelDelegateProtocol <NSObject>

- (void)loginSuccess;
- (void)showTips:(NSString *)tip;
- (void)buttonEnable:(BOOL )enable;

@end
复制代码

新建用于由view调用,viewModel进行响应的protocol


同理,咱们只要确认vm和v须要交换的数据就行了。

  • 用户名输入框的字符串
  • 密码输入框的字符串
  • 点击登录事件
@protocol LoginViewModelInterfaceProtocol <NSObject>

- (void)inputUserName:(NSString *)uname;
- (void)inputPwd:(NSString *)pwd;
- (void)didTapLoginBUtton;

@end
复制代码

实现代理方法

下面咱们新建一个viewModel叫作LoginViewModel

LoginViewModel.h

#import <Foundation/Foundation.h>
#import "LoginViewModelDelegateProtocol.h"
#import "LoginViewModelInterfaceProtocol.h"

@interface LoginViewModel : NSObject<LoginViewModelInterfaceProtocol>

@property (nonatomic ,weak) id<LoginViewModelDelegateProtocol> delegate;

@end
复制代码

LoginViewModel.m

#import "LoginViewModelDelegateProtocol.h"

@interface LoginViewModel ()

@property (assign, nonatomic) BOOL unameValid;

@property (assign, nonatomic) BOOL pwdValid;

@end

@implementation LoginViewModel

- (void)inputUserName:(NSString *)uname {
    self.unameValid = uname.length>0;
    [self judgeAllValid];
}
- (void)inputPwd:(NSString *)pwd {
    self.pwdValid = pwd.length>0;
    [self judgeAllValid];
}
- (void)didTapLoginBUtton {
    // 一些请求,这里忽略网络请求,直接模拟结果
    [self.delegate loginSuccess];
}

- (void)judgeAllValid {
    BOOL v = [self isAllValid];
    [self.delegate buttonEnable:v];
}

- (BOOL)isAllValid {
    return self.unameValid && self.pwdValid;
}

@end
复制代码

而后在controller初始化,而且实现所有的方法就能够了。

viewController.m

#import "viewController.h"
...

@interface viewController () <LoginViewModelDelegateProtocol>
@property (nonatomic ,strong) LoginViewModel *vm;
@end

@implementation TDFSetPhoneNumController

- (void)viewDidLoad {
    
    [super viewDidLoad];
    self.vm.delegate = self;
}

#pragma mark - VMDelegate

- (void)loginSuccess {
    [self.navigationController pushViewController:[SuccessVC new]] animated:true];
}
- (void)showTips:(NSString *)tip {
    [self showAlert:tip];
}
- (void)buttonEnable:(BOOL )enable {
    self.loginbutton.enable = enable;
}

#pragma mark - Getter

- (LoginViewModel *)vm {
    if (!_vm) {
        _vm = [LoginViewModel new];
    }
    return _vm;
}
@end
复制代码

大功告成


这里所有的代码是我手写的,后面省略了一些UIKit相关的布局,想必以你的聪明才智应该已经能很轻松的将剩余的补全了吧。

注意事项

这里有几点我认为应该注意的:

  • vm与v之间不论经过什么传递值和响应,都要保持数据的单向流动。
  • 代理用weak,内存回收时会自动置为nil,
  • 所有model与viewModel中不该包含任何UIKit框架下的类

总结

总而言之,我认为MVVM在咱们的代码总体分工和应用架构的过程当中应用仍是十分优雅和安全的,

不过话说回来什么架构也罢,仍是要看咱们怎么去用它,不是吗

相关文章
相关标签/搜索