iOS架构模式——MV(X)的了解和实战。iOS 的架模式(揭秘 MVC,MVP,MVVM 和 VIPER)

作一个iOS程序员,MVC一定是我们耳熟能详的平等种架构模式,而且当你的类规模无特别之上,MVC也着实来其的优势,它的开效率真的是够高。但当你的类别发展之必然之范畴,你会意识传统的MVC模式会促成C层代码量剧增,维护困难等一样文山会海题材,这个上咱们就需要考虑有旁模式了。

序言

前看了平等篇国外大牛Bohdan
Orlov写的有关
iOS 架构模式之稿子,内容涉及目前 iOS
端诸多主流的模式,个人感觉文章写的不得了对,收获不浅,希望会由此翻原文的方法又好的体味一下,也享受给重新多之人头参考。原文地址在这里,并附着相关PPT,浏览原文可能得正确上网。

MV(X)的基本要素

常用之架构模式

  • MVC
  • MVVM
  • MVP
  • VIPER

前方三种模式都是因为三个模块组成:

  • Models —— 数据层,负责数据的处理。
  • Views —— 展示层,即具有的UI
  • Controller/Presenter/ViewModele(控制器/展示器/视图模型)——它们背负View与Mode之间的调遣

正文

在 iOS 开发中动用 MVC 是否感到特别奇怪?对 MVVM 感到有问题?听说过
VIPER,但是以未确定它们是否来价?继续读书本文,你以见面找到这些题目之答案,如果无找到如意的答案,请在评论被管吐槽吧。本文将协助您建从关于
iOS
端架构模式之知识体系。我们先来概括地回顾一些主流的架构,并且于理论与部分小例子的尽上展开比。

注意:
学设计模式是进阶阶段,因此在翻阅本文之前,假要你曾经起得的功底,不会见重复了解如下的题材:
1、谁当有着网络要,Model 还是 Controller?
2、如何给View的ViewModel传递Model?
3、谁会创造一个VIPER模块:Router 还是 Presenter?

MVC

为何要体贴选择哪的架构

若无关心架构,想象某天若调试一个宏伟的类,里面来正数十只例外关联东西,你见面发觉几乎不可能定位问题点并修复bug。当然,你吧老为难去随心所欲地使用这仿佛,因为不了解类其中的有至关重要细节。如果您以档次遭到早已遇到了这种气象,它或许是诸如这么的:

1、这个类似是UIViewController的子类
2、数据直接存储于UIViewController中
3、视图View没有外操作
4、Model的数据结构设计好糟糕
5、没有单元测试覆盖

即你按照了苹果指导意见并贯彻了苹果的 MVC
模式,这种状态要可能会见有,不必觉得颇不便了。苹果之
MVC 有点问题,我们回头再说就起工作。让我们来定义一个吓搭应该有的特征:

1、严格划分,均衡分配实体间的角色与天职
2、可测性通常是第一特性(不要担心:好搭一定有可测性)
3、便于使用,且维护成本低

传统的MVC

咱们所熟悉的MVC其实Apple给咱们提供的Cocoa
MVC,但实际MVC最优先有于Web,它原先的样子应该是这么的

图片 1

传统MVC

于这种架构下,View是不管状态的,在Model变化之上它只是简单的吃Controller重绘,比如网页遭到您点击了一个初的链接,整个页面就再度加载。尽管这种MVC在iOS应该里面可以实现,但是由MVC的老三单模块都密不可分耦合了,每一个模块都跟另外少种模块出关联,所以就是贯彻了也绝非啊含义。这种耦合还降了它的只是重用性,所以,传统的MVC在iOS中好舍了。

何以要分开

当试图了解程序如何运作时,角色跟职责分开能够给我们保持思路清晰。如果你的开发力量越强,你虽更能够领悟复杂的物。但是这种力量并无是线性增长的,会飞达成顶峰。因此下降复杂性的最简易方法是准纯净责任原则,划分多独实体之间的天职。

Apple的MVC

图片 2

Cocoa MVC

Apple提供的MVC中,View和Model之间是相独立的,它们只是透过Controller来互联系。可惜的是Controller得重用性太差,因为咱们一般还管乱的政工逻辑在了Controller中。

具体中,我们的MVC一般是这么的

图片 3

现实MVC

怎会这么吧?主要还是盖咱们的UIViewController它自己便拥有一个VIew,这个View是独具视图的根视图,而且View的生命周期也还出于Controoler负责管理,所以View和Controller是颇为难做到互相独立的。虽然您可以将控制器里的部分事情逻辑与数码易工作授Model,但是你却没办法用有些干活让View来平摊,因为View的主要职责只是拿用户之操作行为付出Controller去处理而已。于是Controller最终就成了具有东西的代办和数据源,甚至还闹网络要求…..还有……所以我们描绘的Controller代码量一般都是坏酷的,随着当工作要求的充实,Controller的代码量会一直增长,而相对来说View及Model的代码量就比较稳定,所以啊有人把MVC叫做Massive
View Controller,因为Controller确实显示有些臃肿。

每当此处关于Model的分,其实有一个肥胖Model和薄Model之分,它们的差距主要就是是把Controller的部分数据处理职责交给了肥胖Model。

胖Model(Fat Model):

肥胖Model包含了有弱业务逻辑。胖Model要达成的目的是,Controller从肥胖Model这里将到多少以后,不用做额外的操作还是只开生少之操作就会拿数据以在View上。
FatModel做了这些已故业务后,Controller可以转换得相对skinny一点,它才待关怀大业务代码。而大业务转移的可能性要比较死业务大得多,弱业务相对稳定,所以弱业务塞给Model不会见产生极好问题。另一方面,弱业务再次出现的效率要大于强业务,对复用性要求重复强,如果这一部分政工形容在Controller,会招致代码冗余,类似之代码会落得四处都是,而且一旦弱业务产生涂改,你尽管见面待修改所有地方。如果塞到了Model中,就单纯待转移Model就够用了。
可胖Mpdel也无是就从未缺陷的,它的短就是在于胖Model相对比较为难移植,虽然独自是富含弱业务,但是其毕竟为是工作,迁移的时刻非常容易拔出罗布带起泥,也就是说它耦合了其的事务。而且软件是会成长之,FatModel也十分有或随着软件的成人尤为Fat,最后难以维护。

瘦Model(Slim Model):

瘦Model只承担作业数据的发表,所有事务无论强弱一律口吃Controller。瘦Model要上的目的是,尽一切恐怕失去编写精心粒度Model,然后配套各种helper类或者措施来对死去业务做抽象,强业务还是交给Controller。
由于Slim
Model跟工作全无关,它的数量可以交给其他一个可知处理它数的Helper或另的目标,来形成作业。在代码迁移的时候独立性很强,很少会面世拔出萝卜带出泥的景况。另外,由于SlimModel只是数量表达,对它进行保障基本上是0成本,软件膨胀得再决定,SlimModel也无见面死到哪里去。缺点就是在于,Helper这种做法呢掉得甚好,由于Model的操作会面世于各种地方,SlimModel很轻并发代码重复,在必程度达违反了DRY(Don’t
Repeat
Yourself)的思绪,Controller仍然不可避免在早晚水准及面世代码膨胀。

综合,Cocoa MVC在各个地方的显现如下:

  • 划分 – View 和 Model 确实是促成了分手,但是 View 和 Controller
    耦合的太 厉害
  • 可测性 – 因为划分的不够明亮,所以能测的着力就是只有 Model 而都
  • 易用
    相较于外模式,它的代码量最少。而且基本上每个人还很熟悉她,即便是绝非太多经历的开发者也会维护。
何以而可测性

于那些由于补加新特色,或者局部正重构中的纷繁的接近来说,开发人员应该感激出现破产的单元测试,因为这些失败的单元测试可以帮忙开发人员尽快定位运行中出现的bug,而这些bug可能出现于用户的配备上,甚至用花数到才会修补。

MVP

图片 4

MVP

在押起和Cocoa
MVC很像,也确非常像。但是,在MVC中View和COntroller是一体耦合的,而以MVP中,Presenter完全不关心ViewController的生命周期,而且View也能于概括mock出来,所以当Presenter里面基本无呀布局相关的代码,它的职责只是经过数量和状态更新View。
还要每当MVP中,UIVIewController的那些子类其实是属于View的。这样即使提供了又好之可测性,只是开发速度会重胜,因为若必手动去创造数量以及绑定事件。

下我形容了只简易的Demo

图片 5

MVPDemo

鉴于此根本是习架构模式思想,所以自己的命名简单粗暴,希望大家知晓。

图片 6

界面1

界面也颇简短,就是经过点击按钮修改两单label显示的内容

Model很简短,就是一个数据结构,但在事实上用中,你可以以网络要等有数处理在此处

@interface Model : NSObject

@property (nonatomic, strong) NSString *first;
@property (nonatomic, strong) NSString *second;

@end

倘若让Presenter和View通信,所以我们定义一个商议,以实现Presenter向View发送命令

@protocol MyProtocol <NSObject>

- (void)setFirst:(NSString *)first;
- (void)setSecond:(NSString *)second;

@end

view/VIewController,实现该谋

.h
 @interface ViewController : UIViewController

@property (nonatomic, strong) UILabel *firstLabel;
@property (nonatomic, strong) UILabel *secondLabel;
@property (nonatomic, strong) UIButton *tapButton;

@end


.m主要代码
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.firstLabel];
    [self.view addSubview:self.secondLabel];
    [self.view addSubview:self.tapButton];
    self.presenter = [Presenter new];
    [self.presenter attachView:self];
}

- (void)buttonClicked{
    [self.presenter reloadView];
}

- (void)setFirst:(NSString *)first{
    self.firstLabel.text = first;
}

- (void)setSecond:(NSString *)second{
    self.secondLabel.text = second;
}

Presenter

.h
@interface Presenter : NSObject

- (void)attachView:(id <MyProtocol>)attachView;
- (void)reloadView;

@end


.m
@interface Presenter()

@property (nonatomic, weak) id <MyProtocol> view;
@property (nonatomic, strong) Model *model;

@end

@implementation Presenter

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.model = [Model new];
        self.model.first = @"first";
        self.model.second = @"second";
    }
    return self;
}

- (void)attachView:(id<MyProtocol>)attachView{
    self.view = attachView;
}

- (void)reloadView{
    //可以在这里做一些数据处理
    [self.view setFirst:self.model.first];
    [self.view setSecond:self.model.second];
}
@end

这边就是一个简练的Demo,其实想想很简单,就是说话业务逻辑交给Presenter,而Presenter以令的花样来控制View。
完整Demo可以看这里

怎么要易用

这不需要应对,但值得一提的是,最好的代码就是绝不写代码,因此写的更是少越不容易错。这象征想写少量代码的想法不仅仅是盖开发者的好逸恶劳,而且若吗不应该被一个再次活的化解方案所蒙蔽,而忽视了保障它的本金。

一部分认证:

MVP架构拥有三单真独立的旁,所以在组建的时候会有一对问题,而MVP也成为了第一单披露这种题材之架构,因为我们不思量让View知道Model的信,所以于时底Controller去组装是不正确的,我们该以另外的地方得组建。比如我们得以创造一个应用层的Router服务,让其来当组建和View-to-View的转场。这个问题下洋洋模式面临还存在。

下面总结一下MVP的诸面呈现:

  • 划分——我们把大部分任务都分配至了Presenter和Model里面,而View基本不待开啊
  • 可测性——我们得经View来测试大部分事务逻辑
  • 易用——代码量差不多是MVC架构的蝇头倍,但是MVP的思路或很清晰的

此外,MVP还有一个变体,它的不比主要就是填补加了数量绑定。这个本的MVP的View和Model直接绑定,而Presenter仍然持续处理View上的用户操作,控制View的展示变化。这种架构和风俗的MVC类似,所以我们着力可舍。

MV(X)系列导论

今日当我们要开架构设计时有很多种模式选择:

  • MVC
  • MVP
  • MVVM
  • VIPER

前面三者采用的都是把App中实体划分成3类:

  • Models
    负责持有数量,进行多少处理的数量访问层。设想一下PersonPersonDataProvider类。
  • Views – 负责数据显现层(Graphical User
    Interface),在iOS端可当颇具坐UI前缀的近乎。
  • Controller/Presenter/ViewModel
    负责协调处理ModelsViews里的相。

一般用户操作视图会触发数据更新,数据的反而会引起视图更新。这样的划分实体能让我们:

  • 双重好的喻她们是什么样行事之
  • 复用他们(通常只是复用的是ViewsModels
  • 独测试他们

于咱们初步念MV(X)模式,稍后再说VIPER

MVVM

MVVM可以说凡是MV(X)系列中时髦兴起的也罢是极端出彩的等同种植架构,而她呢广受我们iOS程序员喜爱。

图片 7

MVVM

MVVM和MVP很像:

  • 把ViewController看成View
  • View和Model之间从未紧耦合

除此以外它们还受VIew和ViewModel做了数额绑定。ViewModel可以调用对Model做变更,也堪又Model更新的时段对自身进行调,然后通过View和ViewModel之间的绑定,对View进行对应的更新。

一、MVC(Model-View-Controller)

以讨论Apple版本的MVC之前,我们先行来探传统的MVC

图片 8

Traditional MVC

图示中,视图Views凡是无论状态的,它只是当数Models发生变化时,通过Controller决定简单地表现一下。设想当您点击网页上有跳反链接时,整个网页就会又加载。虽然于iOS应用程序中这种习俗的MVC很爱实现,但随即是尚未意思之,因为架构上3类实体紧密的耦合在一起,每一样好像实体都设与其它两看似产生关联,这会大大降低代码的但是复用性。这不见面是您想要之架构,由于以上原因,我们就是无写这种MVC的典型例子了。

风土的MVC不切合当下的iOS开发工作

有关绑定

每当iOS平台方面有KVO和通知,但是之所以起总是觉得无绝便宜,所以有局部叔在库供我们选:

  • 冲KVO的绑定库,如
    RZDataBinding
    或者
    SwiftBond
  • 以全量级的
    函数式响应编程
    框架,比如ReactiveCocoaRxSwift
    或者PromiseKit

其实,我们于关系MVVM的时段就是生爱想到ReactiveCocoa,它吗是咱们以iOS中应用MVVM的绝好工具。但是相对来说它的读书成本与掩护成本
也是较大的,而且只要你以不当,很可能导致灾难性的题目。

脚我临时不要RAC来简单展示一下MVVM:

图片 9

MVVM

界面很简短,就是点击一个button修改label里面的多少

图片 10

界面

Model

@interface MVVMModel : NSObject

@property (nonatomic, copy) NSString *text;

@end

@implementation MVVMModel

- (NSString *)text{
    _text = [NSString stringWithFormat:@"newText%d",rand()];
    return _text;
}

ViewModel

@interface MVVMViewModel : NSObject

- (void)changeText;

@end

@interface MVVMViewModel()

@property (nonatomic, strong) NSString *text;
@property (nonatomic, strong) MVVMModel *model;

@end

@implementation MVVMViewModel

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.model = [MVVMModel new];
    }
    return self;
}

- (void)changeText{
    self.text = self.model.text;;
}

Controller

@interface MVVMViewController ()

@property (weak, nonatomic) IBOutlet UILabel *textLabel;
@property (nonatomic, strong) MVVMViewModel *viewModel;

@end

@implementation MVVMViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.viewModel = [[MVVMViewModel alloc]init];
    [self.viewModel addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:nil];
}
- (IBAction)buttonClicked:(UIButton *)sender {
    [self.viewModel changeText];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    self.textLabel.text = change[@"new"];
}

MVVM的核心就是View和ViewModel的一个绑定,这里我只是简短的经过KVO实现,看起连无是那优雅,想只要深应用的讲话我看还是发必不可少学习一下RAC底,需要总体的Demo请看这里。

下面我们重新来对MVVM的诸面呈现做一个品:

  • 划分——MVVM 框架之中的 View 比 MVP
    里面负责的事体若重复多片。因为前者是透过 ViewModel
    的多寡绑定来更新自己状态的,而后者只是把有的事件都付给 Presenter
    去处理便终止了,自己本身并无负担更新。
  • 可测性—— 因为 ViewModel 对 View
    是大惑不解的,这样我们对其的测试就变得很简单。View
    应该吗是能给测试的,但是或许因为其对 UIKit
    的借助,你见面一直略过它们。
  • 易用——它比MVP会更加简洁,因为当 MVP 下您必须使拿 View
    的具有事件还付 Presenter 去处理,而且用手动的去创新 View
    的状态;而当 MVVM 下,你一味待为此绑定就可以解决。

综上:MVVM
真的好有魅力,因为它不仅仅成了上述几栽框架的助益,还非欲你吗视图的换代去写额外的代码(因为在
View 上都举行了数额绑定),另外它于可测性上的见吗还非常棒。

以简单好亮,以上的Demo都深简短,不了解看了马上首博客能否加深你针对MV(X)的片段明亮,这些理解啊无非作为自己个人的一部分参照,有啊尴尬的地方想大家指出。

Apple版的MVC

Apple期望的Cocoa MVC

图片 11

Cocoa MVC

控制器Controller是视图Views和数据Models里头的中介,它们中间未欲来关系。可复用性最低的控制器Controller,通常是可以承受之,因为咱们不能不有一个地方来放置那些休切合放在数据Models遭逢的有着复杂工作逻辑。理论及,它看起来非常简单明了,你是休是发到发啊问题?甚至听到过有人叫
MVC 为重控制器模式。此外,对于 iOS
开发者来说,给控制器减轻负担已经成一个根本之话题。为什么苹果会采用单独改进了一点点底人情
MVC 模式为?实际上的Realistic Cocoa MVC

图片 12

Realistic Cocoa MVC

Cocoa
MVC
勉励而写重控制器是为她于Views的生命周期中相互依赖,以至于很麻烦将它们分别。虽然你恐怕有法子将部分事务逻辑与数量转模型的劳作放到Models遭到,但是于分摊至Views落得的工作却没有啊办法,大多数情形下,Views的保有机能就是被控制器Controller出殡操作事件,而Controller末见面化你可以想到所有东西的代理要数据源,比如一般会承担发送或取消网络要等等。你时不时会面盼如此的代码:

var userCell = tableView.dequeueReusableCellWithIdentifier("identifier") as UserCell
userCell.configureWithUser(user)

cell 作为一个视图Views直白通过Models进展布置,MVC
的尺度为违反了,但这种状态一直以生,大家呢无当有啊错。如果您严格的信守
MVC,那么你就需经过Controller对 cell
进行布置,并且不把Models传进Views遭受,然而当下将会还进一步地增加Controller的规模。

Cocoa MVC如作重控制器模式还是生必然道理的。

问题直到需要展开单元测试了才会暴露出来(希望您的种类为同)。由于ControllerViews严谨的耦合在一起,单元测试变得不可开交拮据,因为您将不得不非常有想象力的失去学Views的生命周期,写Controller测试代码时为得尽量把业务逻辑代码和Views的布局代码分离开。让咱来拘禁一个简约的playground例子:

import UIKit

struct Person { // Model
    let firstName: String
    let lastName: String
}

class GreetingViewController : UIViewController { // View + Controller
    var person: Person!
    let showGreetingButton = UIButton()
    let greetingLabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
    }

    func didTapButton(button: UIButton) {
        let greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
        self.greetingLabel.text = greeting

    }
    // layout code goes here
}
// Assembling of MVC
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
view.person = model;

MVC架构可以当视图控制器中进行组装

马上段代码看上去不可测,对吧?我们可将转变greeting字符串的代码封装到新的GreetingModel仿佛吃独测试其。但是在无直调用视图UIView连锁办法(viewDidLoaddidTapButton这些艺术恐怕会见加载所有视图)的前提下,我们或无法测试GreetingViewController个中任意的见逻辑(虽然这个事例没有小逻辑),这不便宜单元测试。实际上,在一个模拟器(例如:iPhone
4S)上之测试并无克确保在外设备(例如:iPad)上啊能运作良好。因此建议于单元测试中去Host Application的配置,并且测试用例不要运行在模拟器上。

视图和控制器之间的彼此并无是真的的单元测试

归纳,Cocoa
MVC
有如是一个一定糟糕之模式。让咱们用文章开始提到的好搭特征来对它们进行一个评估:

  • 划分 –
    ViewModel审是分离了,但是ViewController要么严谨地耦合在一起。
  • 唯独测试性 – 由于分的糟糕,你也许只能测试你的Model
  • 易用性 –
    相比于任何模式代码量最小,此外门槛低,每个人还能够熟练掌握,即使不是一个特别有经历的开发者也会展开保障。

一旦对你的微项目,不打算投入多时去规划架构,也无打算投入极其多本去保护,那么Cocoa
MVC
是你要是选择的模式。

每当开速度上,Cocoa MVC凡是最好之架构模式。

二、MVP(Model-View-Presenter)

Cocoa MVC的演变

图片 13

Passive View variant of MVP

看起来是勿是充分像Cocoa MVC?的确颇像,只是称MVP(Passive View
Variant)。稍等。。。这是不是意味着MVP的精神就是是Cocoa
MVC
否?当然不是,因为您想起一下ViewController一体耦合在一起的职务,在MVP中是Presenter
,它同视图控制器的生命周期没有其余关系,并且由于无另外布局之代码,很容易模仿视图View。它的任务是创新View面临的数目及状态。

苟我报告您UIViewController不怕视图,会怎么样

MVP方面,UIViewController的子类实际上是视图而休是Presenter。这种区别提供了大好之可测性,但会跌自然的付出速度,因为若只能手动管理数据以及绑定事件。举个例子:

import UIKit

struct Person { // Model
    let firstName: String
    let lastName: String
}

protocol GreetingView: class {
    func setGreeting(greeting: String)
}

protocol GreetingViewPresenter {
    init(view: GreetingView, person: Person)
    func showGreeting()
}

class GreetingPresenter : GreetingViewPresenter {
    unowned let view: GreetingView
    let person: Person
    required init(view: GreetingView, person: Person) {
        self.view = view
        self.person = person
    }
    func showGreeting() {
        let greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
        self.view.setGreeting(greeting)
    }
}

class GreetingViewController : UIViewController, GreetingView {
    var presenter: GreetingViewPresenter!
    let showGreetingButton = UIButton()
    let greetingLabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
    }

    func didTapButton(button: UIButton) {
        self.presenter.showGreeting()
    }

    func setGreeting(greeting: String) {
        self.greetingLabel.text = greeting
    }

    // layout code goes here
}
// Assembling of MVP
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
let presenter = GreetingPresenter(view: view, person: model)
view.presenter = presenter
有关做的重要说明

MVP是首先独公布了事实上由3个单身分会设有组合问题的模式。既然我们并无期望ViewModel耦合,在视图控制器中(实际上是View)组装它们就是未科学的,因此我们用以任何地方处理。例如,我们得让App范围外的路由服务来承担处理View与View之间的变现。这个题材不仅MVP受到在,后面将介绍的保有模式遭遇吗都是。我们来瞧MVP的特征:

  • 分 –
    大部分职责都让剪切给了PresenterModelView没其他职责(例子中之Model也没有职责)。
  • 然测试性 – 很好,我们好测试大部门业务逻辑,因为View无职责。
  • 易用性 –
    在咱们特别的不切实际的事例中,代码量比MVC翻了平等加倍,但与此同时,MVP的计划思路好明晰。

每当iOS开发被使用MVP模式代表可以的可测性和重重底代码量。

根据绑定和监察

再有另外一种样式的MVP模式 —
带监控器的MVP。这种模式之特点包括直接绑定ViewModel,同时Presenter(监控器)仍然控制着View上的操作事件,并能更改View的展现。

图片 14

Supervising Presenter variant of the MVP

可是恰恰而我们之前认识及之,模糊不彻底的天职分配是不好的筹划,ViewModel也紧密的耦合在一起。这种模式与Cocoa桌面端程序支付相似。和风土人情的MVC模式一样,对于有通病的架,我认为并未必要更举例。

三、MVVM(Model-View-ViewModel)

MVVM凡是时髦的MV(X)铺天盖地架构,我们意在它在规划的新便曾考虑到事先的MV(X)系列所面临的问题。从理论及来拘禁,Model-View-ViewModel看起对。ViewModel咱们已好熟悉了,但中层换成了ViewModel

图片 15

MVVM

它和MVP模式大像:

  • 相图控制器划分成View
  • ViewModel以内无紧密的耦合

另外,数据绑定的概念好像带监控器的MVP,不同之是这次绑定的是ViewViewModel,而不是ViewModel。那么当骨子里的iOS开发中ViewModel大凡什么?从根本上来说,它是单身于UIKit可知显现你的View和状态。ViewModel可以调用Model来改变多少,也堪经数量变动来更新自己,因为ViewViewModel拓展了绑定,相应地为就可知一起创新View

绑定

在介绍MVP一部分我简单地涉绑定的定义,但我们还是以此讨论一下它。绑定来源于OS
X开发环境,在iOS开发被没。当然我们得以应用KVO以及消息通知机制,但还不如绑定好。因此,如果我们无思自己实现同学绑定机制,有有限种植选择:

  • 基于KVO的数码绑定库,比如RZDataBinding,SwiftBond
  • 全量级的函数式响应编程框架,比如ReactiveCocoa,RxSwift,PromiseKit

实质上,当你听到MVVM不怕会见联想到ReactiveCocoa,反之亦然。虽然使用ReactiveCocoa框架或是您充分易建立由根据绑定的MVVM,并且达出她的极其特别价值,但响应式框架来一个痛之现实:能力越来越怪,责任吗尽管越是充分。当您以响应式框架的时刻很爱就弄得乱七八糟七八潮,换句话说,如果出现bug,你以见面花大量之岁月错开调试bug,看看下面的调用堆栈图。

图片 16

Reactive Call Stack

当咱们的简便例子中,无论是以函数响应式框架,还是KVO犹生硌大材小用。我们转移另外的法子,通过调用showGreeting措施来要View
Model
更新View,使用greetingDidChange扭动调函数作为简单的属性。

import UIKit

struct Person { // Model
    let firstName: String
    let lastName: String
}

protocol GreetingViewModelProtocol: class {
    var greeting: String? { get }
    var greetingDidChange: ((GreetingViewModelProtocol) -> ())? { get set } // function to call when greeting did change
    init(person: Person)
    func showGreeting()
}

class GreetingViewModel : GreetingViewModelProtocol {
    let person: Person
    var greeting: String? {
        didSet {
            self.greetingDidChange?(self)
        }
    }
    var greetingDidChange: ((GreetingViewModelProtocol) -> ())?
    required init(person: Person) {
        self.person = person
    }
    func showGreeting() {
        self.greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
    }
}

class GreetingViewController : UIViewController {
    var viewModel: GreetingViewModelProtocol! {
        didSet {
            self.viewModel.greetingDidChange = { [unowned self] viewModel in
                self.greetingLabel.text = viewModel.greeting
            }
        }
    }
    let showGreetingButton = UIButton()
    let greetingLabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.showGreetingButton.addTarget(self.viewModel, action: "showGreeting", forControlEvents: .TouchUpInside)
    }
    // layout code goes here
}
// Assembling of MVVM
let model = Person(firstName: "David", lastName: "Blaine")
let viewModel = GreetingViewModel(person: model)
let view = GreetingViewController()
view.viewModel = viewModel

双重来探MVVM的特征:

  • 分开 –
    在咱们这个有些例子中看出底莫是老清楚,但实在,MVVM中的View荷了较MVP设多之天职。首先它需要绑定ViewModel来更新状态,其次它要传递所有的轩然大波信息使未待更新事件的提供者。
  • 唯独测试性 –
    ViewModel并无持有View,这吃咱死轻测试其。View为可以测试,但它依靠UIKit便会忽略掉。
  • 易用性 –
    代码量和MVP一样多,但真正的App开发中使采取绑定机制,去替换那些传递事件及手动更新的代码,会削减过多代码量。

MVVM大凡很吸引人口的,因为它们整合了面前提及的几乎种植模式的优点,此外使用绑定机制不需编制额外的视图更新代码,并且保持了大好的不过测试性。

四、VIPER(View-Interactor-Presenter-Entity-Routing)

自搭积木中领会的iOS设计

VIPER凡是本文最后一个介绍的架模式,它杀风趣,不属MV(X)漫山遍野之扩充。到目前为止,你不能不意识及一个吓的统筹得有细粒度的任务分开。VIPER从旁一个两样之角度开展了任务分开,这次我们分为5层:

图片 17

VIPER

  • 交互器:包含与数据(实体)或网络有关的事情逻辑,比如从服务器获取有初的数码实体,为了这些目的,你会使用一些ServicesManagers,它们并无给看属于VIPER遭遇之平等组成部分,更合适地游说它是一模一样种植额外的倚重。
  • 展示器:包含部分及UI相关(UIKit除)的政工逻辑,通过交互器调用方法。
  • 实体:纯粹的多少对象,不包含数据访问层,因为马上是交互器的天职。
  • 路由器:负责VIPER模块之间的切换。从根本上说,粒度划分方式,VIPER模块可用来计划一个面貌的效益,也得以据此来统筹下被的一个一体化用户故事—比如身份验证,是由于一个场景或者几场景结合,应该为此几近特别之积木块来增加乐高玩具,完全在你。

MV(X)多重对比,我们会发觉在任务分开上出一对不同点:

  • Model
    数据交互逻辑给更换到了交互器Interactor中,Entities只发纯的数据结构。
  • Controller/Presenter/ViewModel中的UI展现职责转移至了交互器Interactor着,但它没有改数据的力。
  • VIPER举凡首先独明确提出地址导航职责应该由路由器Router来解决。

于iOS应用被找到同样种植适于的路由方式是一个挑战,MV(X)文山会海模式都无定点及这个题材。

下面的VIPER事例中没有关联到模块之间的路由或互相,当然在MV(X)多元模式中吗从来没涉嫌。

import UIKit

struct Person { // Entity (usually more complex e.g. NSManagedObject)
    let firstName: String
    let lastName: String
}

struct GreetingData { // Transport data structure (not Entity)
    let greeting: String
    let subject: String
}

protocol GreetingProvider {
    func provideGreetingData()
}

protocol GreetingOutput: class {
    func receiveGreetingData(greetingData: GreetingData)
}

class GreetingInteractor : GreetingProvider {
    weak var output: GreetingOutput!

    func provideGreetingData() {
        let person = Person(firstName: "David", lastName: "Blaine") // usually comes from data access layer
        let subject = person.firstName + " " + person.lastName
        let greeting = GreetingData(greeting: "Hello", subject: subject)
        self.output.receiveGreetingData(greeting)
    }
}

protocol GreetingViewEventHandler {
    func didTapShowGreetingButton()
}

protocol GreetingView: class {
    func setGreeting(greeting: String)
}

class GreetingPresenter : GreetingOutput, GreetingViewEventHandler {
    weak var view: GreetingView!
    var greetingProvider: GreetingProvider!

    func didTapShowGreetingButton() {
        self.greetingProvider.provideGreetingData()
    }

    func receiveGreetingData(greetingData: GreetingData) {
        let greeting = greetingData.greeting + " " + greetingData.subject
        self.view.setGreeting(greeting)
    }
}

class GreetingViewController : UIViewController, GreetingView {
    var eventHandler: GreetingViewEventHandler!
    let showGreetingButton = UIButton()
    let greetingLabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
    }

    func didTapButton(button: UIButton) {
        self.eventHandler.didTapShowGreetingButton()
    }

    func setGreeting(greeting: String) {
        self.greetingLabel.text = greeting
    }

    // layout code goes here
}
// Assembling of VIPER module, without Router
let view = GreetingViewController()
let presenter = GreetingPresenter()
let interactor = GreetingInteractor()
view.eventHandler = presenter
presenter.view = view
presenter.greetingProvider = interactor
interactor.output = presenter

受咱们又来看看VIPER的特征:

  • 划分 – 毫无疑问,VIPER于任务分开上是不过好的。
  • 然测试性 – 毫无悬念,好之天职分开一定产生好的可测性。
  • 易用性 –
    由于上述两只特色你不怕好猜测及代码维护性成本非常高,你只能编写大量的接口类来完成好有些的天职。

总结

我们曾起到尾地了解了几种植架构模式,希望您会从中找到那些既困扰乃充分遥远之问题之答案。但自己不用怀疑,你曾经意识及了没有啊银色子弹,选择什么的架构设计是一定情景下权衡各种因素之后的结果。因此,在和一个app中就是会现出混合架构设计。比如:一始使用MVC,然后您发现有局部破例现象如果采取MVC用会见难以保障,这时你得只有对这个场景下MVVM模式,没必要失去重构那些MVC搭执行之老大好的模块。MV(X)数不胜数是相互配合的。

Make everything as simple as possible, but not simpler. — Albert
Einstein

相关文章

Leave a Comment.