美文网首页
关于MVVM的一些认识

关于MVVM的一些认识

作者: Carrliron | 来源:发表于2018-12-05 10:45 被阅读0次

    在IOS开发之中,初学者总是被教导遵循MVC开发模式,后来又出现了MVVM模式,这些都是什么鬼?虽然如此,但很多人最开始写代码还是将所有的代码揉和在一个viewcontroller里面。既然揉和在一起和分模式写代码都能达到程序的实现,那么为什么要搞这些呢?

    在实际的项目开发中,我们会发现如果全部将代码写在viewcontroller里面,那么一个VC出现三四千行代码都是很正常的,对于之后的维护与修改都是一个很麻烦的过程,楼主曾经接手过一个项目,之前的人就是将所有代码写在一个VC里面,一个VC里面的代码平均有3千多行,多的达到了5千行,当时我很想将项目去重构一次,这个时候你就能够明白用开发模式的重要性了。

    话说回来,什么是MVC呢?这个网上文章一大堆,我就简单的说下,Attention:

    4972583-c15629e86ba4c566.png

    网上找了一张很简单易懂的图片,如图所示,MVC就是将代码分为model(模型),view(视图),controller(控制器)三个模块,model和view不能直接沟通,需要通过controller进行对接。

    view具体呈现的数据取决于model,用户修改model的数据,view上面的内容就会随之而改变,controller负责初始化model,并将model的数据传递给view去解析展示。

    4972583-45e294e4486b9044.jpeg

    装修公司装修房子,项目主管告诉设计师装修的风格,设计师设计之后交给项目主管审核,然后项目主管再将设计图纸交给装修工人,在这个过程中设计师不会直接和装修工人进行交流,项目主管就相当于controller,设计师就是model,装修工人就是view。

    在IOS开发之中,MVC模式主要的目的就是分离分离视图(View)和模型(Model),使用MVC模式我们达到了分离的效果,可是我们在开发当中会发现一个问题,那就是Controller(控制器)的体积越来越大。所以在IOS中的MVC也被称作Massive View Controller。

    下面我们来看一段代码:

    Model:

    #import <Foundation/Foundation.h>
    
    @interface User : NSObject
    
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, copy) NSString *userId;
    
    @end
    

    ViewController:

    #import "MainViewController.h"
    #import "User.h"
    
    @interface MainViewController ()
    
    @property (nonatomic, strong) UITableView *myTableView;
    @property (nonatomic, strong) User *user;
    
    @end
    
    
    @implementation MainViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.initialModel();
    }
    
    - (void)initialModel {
        self.user = [[User alloc] init];
        self.user.name = "lee"
        self.user.userId = "123456"
    }
    
    @end
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
        MainCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    
        cell.nameLabel.text = self.user.name;
        cell.userIdLabel.text = self.user.userId;
    
        return cell;
    }
    

    同时,我们在开发的过程中又会发现一个问题,model里面又太轻量了,这就形成了头重脚轻,最大的一个问题来自于单元测试,由于Controller混合了业务逻辑和视图处理逻辑,所以在单元测试过程中分离这些成为了巨大的一个问题,当然,我们可以选择忽略它,那就是不进行单元测试。。。。

    那么怎么办呢,于是就有了MVVM,我们需要把Controller进行优化、瘦身。

    顾名思义,就是在MVC的基础上将Controller继续进行拆分,拆出来一个ViewModel,让我们将视图 UI 和业务逻辑分开。在MVVM架构中,View与ViewController均不能直接引用Model,而是通过引用ViewModel来间接引用Model。

    简单来说,就是API请求完数据,解析成Model,之后在ViewModel中转化成能够直接被视图层使用的数据,交付给前端(View层)。

    我们现在来看看MVVM的优点

    1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。

    2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。

    3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成代码。

    4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

    然后我们来看一看MVVM的框架图


    timg.jpeg

    对比上面MVC的框架图我们可以发现MVC中Controller需要做太多得事情,表示逻辑、业务逻辑,所以代码量非常的大。而MVVM则拆分了Controller里面的很多业务分给ViewModel
    然后我们来看看MVVM下的代码:
    Model:

    #import <Foundation/Foundation.h>
    
    @interface User : NSObject
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, copy) NSString *userId;
    
    @end
    

    ViewModel:

    #import <Foundation/Foundation.h>
    #import "User.h"
    
    @interface UserViewModel : NSObject
    
    @property (nonatomic, strong) User *user;
    
    - (instancetype)initWithUserName:(NSString *)userName userId:(NSInteger)userId;
    
    @end
    
    #import "UserViewModel.h"
    
    @implementation UserViewModel
    
    - (instancetype)initWithUserName:(NSString *)userName userId:(NSInteger)userId {
        self = [super init];
        if (!self) return nil;
        _user = [[User alloc] init];
        if (userName.length > 0) {
            _user.name = userName;
        }else {
            _user.name = @"未知";
        }
        _user.userId = userId;
        return self;
    }
    
    @end
    

    Controller:

    #import "MainViewController.h"
    #import "UserViewModel.h"
    
    @interface MainViewController ()
    
    @property (nonatomic, strong) UITableView *myTableView;
    @property (nonatomic, strong) UserViewModel *userViewModel;
    
    @end
    
    @implementation MainViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.initialViewModel();
    }
    
    - (void)initialViewModel {
        _userViewModel = [[UserViewModel alloc] initWithUserName:@"lee" userId:123456];
    }
    
    @end
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
        MainCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    
        cell.nameLabel.text = self.userViewModel.name;
        cell.userIdLabel.text = self.userViewModel.userId;
    
        return cell;
    }
    

    根据代码我们可以看见,我们把原本在Controller里面应该处理的逻辑业务分离了出来,这样就完成了对Controller里面代码的瘦身,总体上减少了代码的复杂性。

    但是我们也发现了一些问题。对于很简单的界面使用MVVM会增加代码量,但如果界面中内容很多、Cell样式也很多的情况下使用MVVM可以很好地将VC中处理Cell相关的工作分离出来。

    所以,我们现在来看看MVVM的缺点:

    1. 数据绑定使得Bug 很难被调试。你看到界面异常了,有可能是你 View 的代码有 Bug,也可能是 Model 的代码有问题。数据绑定使得一个位置的 Bug 被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。
    2. 数组内容的转化成本较高:数组里面每项都要转化成Item对象,如果Item对象中还有类似数组,就很头疼。
    3. 转化之后的数据在大部分情况是不能直接被展示的,为了能够被展示,还需要第二次转化。
    4. 只有在API返回的数据高度标准化时,这些对象原型(Item)的可复用程度才高,否则容易出现类型爆炸,提高维护成本。
    5. 同一API的数据被不同View展示时,难以控制数据转化的代码,它们有可能会散落在任何需要的地方
    6. 调试时通过对象原型查看数据内容不如直接通过NSDictionary/NSArray直观。

    还有一些具体的问题可以参考Casa Taloyum大神的博客《iOS应用架构谈 网络层设计方案》

    相关文章

      网友评论

          本文标题:关于MVVM的一些认识

          本文链接:https://www.haomeiwen.com/subject/yedicqtx.html