betway必威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,它原本的法应该是这般的

betway必威 1

传统MVC

当这种架构下,View是无状态的,在Model变化的当儿它只是略的受Controller重绘,比如网页遭到若点击了一个新的链接,整个页面就再也加载。尽管这种MVC在iOS应该里面可以实现,但是由于MVC的老三个模块都紧紧耦合了,每一个模块都和外少种植模块出关联,所以就是贯彻了为尚未呀含义。这种耦合还降了它的但是重用性,所以,传统的MVC在iOS中可以放弃了。

为何要分

当试图了解程序如何运转时,角色和职责分开能够给咱保障思路清楚。如果你的开支力量尤为强,你就算愈会理解复杂的物。但是这种能力连无是线性增长之,会迅速达成极端。因此下降复杂性的不过简单易行方法是仍纯责任原则,划分多只实体之间的天职。

Apple的MVC

betway必威 2

Cocoa MVC

Apple提供的MVC中,View和Model之间是彼此独立的,它们才经Controller来互关系。可惜的是Controller得重用性太差,因为我们一般还把乱的作业逻辑在了Controller中。

切切实实中,我们的MVC一般是这般的

betway必威 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

betway必威 4

MVP

在押起和Cocoa
MVC很像,也着实蛮像。但是,在MVC中View和COntroller是一体耦合的,而于MVP中,Presenter完全不关心ViewController的生命周期,而且View也克于略去mock出来,所以在Presenter里面基本没啊布局相关的代码,它的任务只是通过数量与状态更新View。
而在MVP中,UIVIewController的那些子类其实是属View的。这样尽管提供了重新好之可测性,只是开发速度会再也强,因为若要手动去创造数量与绑定事件。

脚我勾勒了单简单的Demo

betway必威 5

MVPDemo

出于此处主要是读书架构模式思想,所以自己的命名简单粗暴,希望大家掌握。

betway必威 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程序员喜爱。

betway必威 7

MVVM

MVVM和MVP很像:

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

此外它还深受VIew和ViewModel做了数码绑定。ViewModel可以调用对Model做改变,也堪还Model更新的时节对我进行调,然后经过View和ViewModel之间的绑定,对View进行对应的换代。

一、MVC(Model-View-Controller)

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

betway必威 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:

betway必威 9

MVVM

界面很粗略,就是点击一个button修改label里面的多寡

betway必威 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

betway必威 11

Cocoa MVC

控制器Controller是视图Views和数据Models次的中介,它们之间不需要来涉嫌。可复用性最低的控制器Controller,通常是足以领之,因为咱们要出一个地方来放那些休相符在数据Models蒙之具有复杂工作逻辑。理论及,它看起来非常简单明了,你是不是深感到发出什么问题?甚至听到了有人叫
MVC 为重控制器模式。此外,对于 iOS
开发者来说,给控制器减轻负担已经改为一个要的话题。为什么苹果会采用单独改进了一点点底风
MVC 模式也?实际上的Realistic Cocoa MVC

betway必威 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的演变

betway必威 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的展现。

betway必威 14

Supervising Presenter variant of the MVP

但巧使我辈事先认识及的,模糊不根本的天职分配是不好的计划性,ViewModel为密不可分的耦合在一起。这种模式及Cocoa桌面端程序开发相似。和人情的MVC模式一样,对于来通病的架构,我以为没有必要再举例。

三、MVVM(Model-View-ViewModel)

MVVM大凡风靡的MV(X)漫山遍野架构,我们盼望她于筹划的新即已经考虑到事先的MV(X)层层所面临的题目。从理论及来拘禁,Model-View-ViewModel看起是。ViewModel我们都颇熟稔了,但中层换成了ViewModel

betway必威 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,看看下面的调用堆栈图。

betway必威 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层:

betway必威 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