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