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

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注