架构方式,的领会与实战

用作三个iOS程序员,MVC一定是我们纯熟的一种架构格局,而且当您的品类规模不大的时候,MVC也确确实实有它的优势,它的支出成效的确是十足高。但当你的项目提升的必定的框框,你会发觉古板的MVC方式会导致C层代码量剧增,维护困难等一星罗棋布难点,这些时候大家就须求考虑部分别样格局了。

Make everything as simple as possible, but not simpler — Albert
Einstein
把每件事,做简单到极致,但又然而分不难 – 阿尔Bert·爱因斯坦

MV(X)的基本要素

常用的架构情势

  • MVC
  • MVVM
  • MVP
  • VIPER

日前二种方式都由五个模块组成:

  • Models —— 数据层,负责数据的拍卖。
  • Views —— 体现层,即全部的UI
  • Controller/Presenter/ViewModele(控制器/浮现器/视图模型)——它们背负View与Mode之间的调配

在采取 iOS 的 MVC 时候感觉奇怪?想要尝试下 MVVM?以前听旁人讲过
VIPE大切诺基,可是又纠结是或不是值得去学?

MVC

持续读书,你就会清楚地方难题的答案 –
如果读完了如故不知底的话,欢迎留言评论。

传统的MVC

咱俩所熟识的MVC其实Apple给我们提供的Cocoa
MVC,但实质上MVC初叶暴发于Web,它原本的样子应该是那般的

图片 1

传统MVC

在那种架构下,View是无状态的,在Model变化的时候它只是简短的被Controller重绘,比如网页中你点击了二个新的链接,整个页面就再度加载。即使那种MVC在iOS应该里面可以落成,可是出于MVC的两个模块都严密耦合了,每一个模块都和别的三种模块有牵连,所以固然是落到实处了也尚未什么样含义。那种耦合还下降了它们的可重用性,所以,古板的MVC在iOS中可以扬弃了。

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的分开,其实有3个胖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很不难并发代码重复,在必然水平上违反了DPRADOY(Don’t
Repeat
Yourself)的思绪,Controller如故不可防止在自然水准上面世代码膨胀。

汇总,Cocoa MVC在各省方的表现如下:

  • 划分 – View 和 Model 确实是已毕了分手,但是 View 和 Controller
    耦合的太 厉害
  • 可测性 – 因为划分的不够清楚,所以能测的基本就唯有 Model 而已
  • 易用
    相较于别的格局,它的代码量最少。而且大多每种人都很熟悉它,尽管是没太多经历的开发者也能爱惜。

对此设计格局的学习是一件不难上瘾的事体,所以先唤醒您弹指间:在您读完那篇作品之后,大概会比读从前有越来越多的问号,比如:

MVP

图片 4

MVP

看起来和Cocoa
MVC很像,也确确实实很像。然而,在MVC中View和COntroller是严密耦合的,而在MVP中,Presenter完全不关怀ViewController的生命周期,而且View也能被略去mock出来,所以在Presenter里面基本没有怎么布局相关的代码,它的天职只是经过数量和景观更新View。
而且在MVP中,UIVIewController的那个子类其实是属于View的。那样就提供了更好的可测性,只是开发速度会更高,因为你不大概不手动去成立数量和绑定事件。

下边我写了个几乎的德姆o

图片 5

MVPDemo

是因为此地最紧借使上学架构方式思想,所以本人的命名简单狂暴,希望大家精通。

图片 6

界面1

界面也很简单,就是经过点击按钮修改三个label显示的始末

Model很简短,就是2个数据结构,但在事实上行使中,你可以将网络请求等片段数目处理放在此处

@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

那边只是二个总结的德姆o,其实想想很粗略,就是讲业务逻辑交给Presenter,而Presenter以命令的款式来决定View。
完整Demo可以看这里

(MVC)哪个人来承担网络请求:是 Model 依然 Controller?

一些认证:

MVP架构拥有两个真正独立的分支,所以在组建的时候会有部分标题,而MVP也成了第1个揭示那种题材的架构,因为大家不想让View知道Model的消息,所以在当前的Controller去组装是不得法的,大家应该在其它的地点成功组建。比如大家可以创立3个应用层的Router服务,让它来负责组建和View-to-View的转场。这几个题材下过多形式中都存在。

上面总计一下MVP的各方面突显:

  • 划分——大家把大多数职责都分配到了Presenter和Model里面,而View基本不须要做怎么着
  • 可测性——我们得以因此View来测试超过一半事务逻辑
  • 易用——代码量差不离是MVC架构的两倍,不过MVP的思绪如故蛮清晰的

别的,MVP还有1个变体,它的两样主要就是添加了数额绑定。这些本子的MVP的View和Model直接绑定,而Presenter依旧继续处理View上的用户操作,控制View的来得变化。那种架构和历史观的MVC类似,所以大家基本可以扬弃。

(MVVM)作者该怎么去把一个 Model 传递给1个新创设的 View 的 ViewModel?

MVVM

MVVM可以说是MV(X)体系中新型兴起的也是最特出的一种架构,而它也广受我们iOS程序员喜爱。

图片 7

MVVM

MVVM和MVP很像:

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

除此以外它还让VIew和ViewModel做了数量绑定。ViewModel可以调用对Model做变更,也得以再Model更新的时候对本人举办调整,然后经过View和ViewModel之间的绑定,对View举办对应的换代。

(VIPE哈弗)哪个人来顶住创造 VIPE途锐 模块:是 Router 照旧 Presenter?

关于绑定

在iOS平台方面有KVO和通报,不过用起来总是认为不太有利,所以有一些三方库供我们采用:

实则,我们在关系MVVM的时候就很简单想到ReactiveCocoa,它也是我们在iOS中采用MVVM的最好工具。然而相对来说它的学习花费和维护开销也是相比高的,而且如果你利用不当,很大概引致魔难性的难点。

下边小编一时不用RAC来不难显示一下MVVM:

图片 8

MVVM

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

图片 9

界面

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的,需求总体的德姆o请看这里

下边我们再来对MVVM的各市点表现做1个说东道西:

  • 划分——MVVM 框架之中的 View 比 MVP
    里面负责的事体要更多一些。因为前端是通过 ViewModel
    的数目绑定来更新本身情况的,而后人只是把具有的轩然大波统统付给 Presenter
    去处理就完了,本身本人并不负责更新。
  • 可测性—— 因为 ViewModel 对 View
    是不解的,那样大家对它的测试就变得很简短。View
    应该也是力所能及被测试的,可是或许因为它对 UIKit的器重,你会直接略过它。
  • 易用——它比MVP会尤其简明,因为在 MVP 下你必要求把 View
    的有着事件都交给 Presenter 去处理,而且必要手动的去创新 View
    的图景;而在 MVVM 下,你只须求用绑定就足以解决。

综上:MVVM
真的很有吸引力,因为它不但结合了上述两种框架的长处,还不必要你为视图的换代去写额外的代码(因为在
View 上已经做了数量绑定),此外它在可测性上的显现也还是很棒。

为了不难易懂,以上的Demo都10分简短,不清楚看了那篇博客能如故不能加深你对MV(X)的一些领略,这几个明白也仅作为自个儿个人的有些参照,有啥窘迫的地点希望大家提议。


缘何要在意架构的抉择吧?

因为如若你忽视的话,难保一天,你就须要去调节3个伟大无比又不无各个难点的类,然后您会发以往这几个类里面,你一点一滴就找不到也修复不了任何
bug。一般的话,把这样大的八个类作为完全放在脑子里记着是一件尤其坚苦的业务,你总是难免会忘掉一些相比重大的底细。即使你发今后你的行使里面早已上马产出那种光景了,那你很只怕碰着过上边那类难点:

  • 其一类是二个 UIViewController 的子类。
  • 您的数据直接保存在了 UIViewController 里面。
  • 你的 UIViews 好像什么都没做。
  • 你的 Model 只是一个彻头彻尾的数据结构
  • 您的单元测试什么都没有遮盖到

实际就是你根据了 Apple 的设计规范,达成了 Apple 的 MVC
框架,也还是一如既往会遇见下面那些题目;所以也没怎么好衰颓的。Apple 的 MVC
框架 有它本人的瑕疵,但是这么些我们后边再说。

让大家先来定义一下好的框架应该拥有的特征:

  1. 用严厉定义的角色,平衡的将职责 划分 给分歧的实业。
  2. 可测性
    日常取决于上边说的率先点(不用太操心,借使架构哪一天的话,做到这一点并不难)。
  3. 易用 并且尊敬成本低。

干什么要分开?

当大家打算去明白事物的做事原理的时候,划分能够减轻大家的脑瓜儿压力。要是您认为开发的愈来愈多,大脑就越能适应去处理复杂的干活,确实是这么。不过大脑的那种能力不是线性升高的,而且很快就会高达1个瓶颈。所以要拍卖复杂的政工,最好的方法依旧在根据单一权利标准 的尺度下,将它的任务分开到多个实体中去。

何以要可测性?

对于那个对单元测试心存感谢的人来说,应该不会有那上头的疑云:单元测试帮忙他们测试出了新功效里面的错误,可能是帮他们找出了重构的二个复杂类里面的
bug。那代表那几个单元测试辅助那么些开发者们在程序运营之前就发现了难题,那几个题材即使被忽视的话很只怕会提交到用户的装置上去;而修复那个问题,又至少须求一周左右的时间(AppStore
审核)。

缘何要易用

那块没什么好说的,直说一些:最好的代码是那个没有被写出来的代码。代码写的越少,难点就越少;所以开发者想少写点代码并不一定就是因为他懒。还有,当你想用2个相比较聪明 的点子的时候,全完不要忽视了它的保证资金。

MV(X) 的基本要素

于今我们面对架构设计方式的时候有了广大摘取:

第①前三种格局都是把具备的实业归类到了上面二种分类中的一种:

  • Models(模型):数据层,可能负责处理数量的数码接口层。比如
    Person 和 PersonDataProvider 类
  • Views(视图):显示层(GUI)。对于 iOS 来说具有以 UI
    开端的类基本都属于这层。
  • Controller/Presenter/ViewModel(控制器/浮现器/视图模型):它是
    Model 和 View 之间的胶水或许说是中间人。一般的话,当用户对 View
    有操作时它承担去修改相应 Model;当 Model
    的值暴发变化时它承受去革新对应 View。

将实体进行归类之后大家可以:

  • 更好的接头
  • 重用(主要是 View 和 Model)
  • 对它们独立的拓展测试

让本人从 MV(X) 连串起头讲起,最终讲 VIPELacrosse。

MVC – 它原本的楷模

在初叶谈论 Apple 的 MVC
在此之前,大家先来看下传统的MVC

在那种架构下,View 是无状态的,在 Model 变化的时候它只是不难的被
Controller
重绘;就如网页一样,点击了1个新的链接,整个网页就再次加载。即使那种架构能够在
iOS 应用里面完成,不过出于 MVC
的两种实体被严密耦合着,各个实体都和其余二种具有牵连,所以即使是贯彻了也不曾怎么意义。那种紧耦合还戏剧性的滑坡了它们被选定的或然,这说不定不是您想要在团结的采取里面来看的。综上,传统MVC 的例子作者以为也并未要求去写了。

古板的 MVC 已经不吻合当时的 iOS 开发了。

Apple 的 MVC

理想

View 和 Model 之间是相互独立的,它们只通过 Controller
来互相联系。有点恼人的是 Controller
是重用性最差的,因为大家一般不会把冗杂的作业逻辑放在 Model
里面,那就只能够放在 Controller 里了。

力排众议上看那样做一般挺简单的,可是你有没有觉得有个别尴尬?你甚至听过有人把
MVC 叫做重控制器情势。别的关于 ViewController
瘦身

已经变为 iOS 开发者们热议的话题了。为何 Apple
要沿用只是做了一点点革新的历史观 MVC 架构呢?

现实

Cocoa MVC 鼓励你去写重控制器是因为 View
的万事生命周期都亟需它去管理,Controller 和 View
很难形成相互独立。即使您可以把控制器里的片段事情逻辑和数码转换的干活付出
Model,不过你再想把负担往 View 里面分摊的时候就无法了;因为 View
的首要任务就只是讲用户的操作行为付出 Controller 去处理而已。于是
ViewController
最后就改为了颇具东西的代办和数据源,甚至还担负网络请求的倡导和废除,还有…剩下的您来讲。

像下边那种代码你应当不生疏吧:

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

Cell 作为贰个 View 直接用 Model 来成功了自家的布局,MVC
的规则被打破了,那种景况一贯留存,而且还没人觉得有如何难题。即使你是严苛根据MVC 的话,你应有是在 ViewController 里面去安顿 Cell,而不是一贯将 Model
丢给 Cell,当然如此会让你的 ViewController 更重。

Cocoa MVC 被戏称为重控制器形式照旧有缘由的。

难点直到开头单元测试(希望你的品种里面早已有了)之后才开首显现出来。Controller
测试起来很困难,因为它和 View 耦合的太狠心,要测试它的话就需求反复的去
mock View 和 View
的生命周期;而且根据那种架构去写控制器代码的话,业务逻辑的代码也会因为视图布局代码的原因此变得很凌乱。

大家来看上边那段 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 的组装,能够置身眼下正在突显的 ViewController 里面

那段代码看起来不太好测试对吗?大家可以把 greeting
的浮动方法放到四个新类 GreetingModel
里面去独立测试。可是大家只要不调用与 View 相关的措施的话
viewDidLoad, didTapButton),就测试不到 GreetingViewController
里面任何的呈现逻辑(即使在上头那几个例子里面,逻辑已经很少了);而调用的话就可能须求把具备的
View 都加载出来,那对单元测试来说太不利了。

实质上,在模拟器(比如 酷派 4S)上运营并测试 View
的显示并不可以保障在任何设备上(比如
surface)也能好好运维。所以小编指出把「Host
Application」从你的单元测试配置项里移除掉,然后在不运转模拟器的动静下来跑你的单元测试。

View 和 Controller 之间的相互,并不大概确实的被单元测试覆盖。

补充:What’s Worth Unit Testing in Objective-C
?

综上所述,Cocoa MVC
貌似并不是二个很好的选料。可是大家仍然评估一下她在各市点的彰显(在篇章起先有讲):

  • 划分 – View 和 Model 确实是促成了分手,不过 View 和 Controller
    耦合的太狠心
  • 可测性 – 因为划分的不够明亮,所以能测的中央就只有 Model 而已
  • 易用
    相较于其余情势,它的代码量最少。而且大多各个人都很熟识它,尽管是没太多经历的开发者也能保证。
    在那种情况下您可以选择 Cocoa
    MVC:你并不想在架设上消费太多的时光,而且你以为对于你的小品种以来,开支更高的维护资产只是浪费而已。

一经您最尊重的是付出速度,那么 Cocoa MVC 就是您最好的采用。

MVP – 保障了义务分开的(promises delivered) Cocoa MVC

看起来着实很像 Apple 的 MVC 对吧?确实蛮像,它的名字是 MVP(被动变化的
View)。稍等…那几个意思是说 Apple 的 MVC 实际上是 MVP
吗?不是的,回顾一下,在 MVC 里面 View 和 Controller
是耦合紧凑的,可是对于 MVP 里面的 Presenter 来讲,它完全不关心ViewController 的生命周期,而且 View 也能被归纳 mock 出来,所以在
Presenter 里面基本没什么布局相关的代码,它的义务只是经过数量和气象更新
View。

假设我跟你讲 UIViewController 在那边的剧中人物其实是 View
你觉得如何。

在 MVP 架构里面,UIViewController 的那么些子类其实是属于 View 的,而不是
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 架构拥有多少个实在独立的分支,所以在组装的时候会有部分标题,而 MVP
也成了第二个披露了那种难题的架构。因为大家不想让 View 知道 Model
的音讯,所以在当下的 ViewController(角色其实是
View)里面去进行组装肯定是不正确的,大家应当在别的的地点成功组建。比如,大家得以创设2个应用层(app-wide)的
Router 服务,让它来顶住组建和 View-to-View 的转场。那几个标题不光在 MVP
中留存,在接下去要介绍的方式里面也都有其一题材。

让大家来看一下 MVP 在各方面的表现:

  • 划分 – 大家把半数以上的职务都分配到了 Presenter 和 Model 里面,而
    View 基本上不要求做哪些(在地方的例证里面,Model 也什么都没做)。
  • 可测性 – 几乎棒,我们可以透过 View 来测试半数以上的事务逻辑。
  • 易用 – 就大家地点拾分简单的例子来讲,代码量大概是 MVC
    架构的两倍,不过 MVP 的思路照旧蛮清晰的。

MVP 架构在 iOS 中表示极好的可测性和高大的代码量。

MVP – 添加了数据绑定的另二个本子

还留存着另一种的 MVP – Supervising Controller MVP。这些本子的 MVP 包罗了
View 和 Model 的直接绑定,与此同时 Presenter(Supervising
Controller)如故三番五次处理 View 上的用户操作,控制 View 的来得变化。

唯独大家前边讲过,模糊的义务分开是不佳的工作,比如 View 和 Model
的紧耦合。这几个道理在 Cocoa 桌面应用开发方面也是均等的。

就如古板 MVC
架构一样,作者找不到有如何说辞须要为那一个有通病的架构写一个例证。

MVVM – 是 MV(X) 种类架构里面最新兴的,也是最特出的

MVVM
架构是 MV(X) 里面最新的3个,让我们盼望它在出现的时候已经考虑到了 MV(X)
格局之前所遭遇的题材吗。

辩驳上来说,Model – View – ViewModel 看起来十二分棒。View 和 Model
大家曾经都熟稔了,中间人的角色我们也如数家珍了,但是在此处中间人的角色变成了
ViewModel。

它跟 MVP 很像:

  • MVVM 架构把 ViewController 看做 View。
  • View 和 Model 之间平昔不紧耦合

其它,它还像 Supervising 版的 MVP 那样做了数码绑定,然而这一次不是绑定
View 和 Model,而是绑定 View 和 ViewModel。

那就是说,iOS 里面的 ViewModel 到底是个如何事物呢?本质上来讲,他是独立于
UIKit 的, View 和 View 的情状的贰个突显(representation)。ViewModel
能积极调用对 Model 做变更,也能在 Model
更新的时候对自己进行调整,然后通过 View 和 ViewModel 之间的绑定,对 View
也举行对应的换代。

绑定

自小编在 MVP
的有些简单的提过那几个内容,在此地让我们再延伸商量一下。绑定那些定义来源于
OS X 平台的花费,不过在 iOS
平台上边,我们并没有相应的开发工具。当然,大家也有 KVO 和
通告,可是用那一个措施去做绑定不太有利。

这就是说,若是大家不想协调去写他们的话,上面提供了多少个挑选:

  • 选3个基于 KVO 的绑定库,比如 TiguanZDataBinding 只怕 SwiftBond。
  • 行使全量级的 函数式响应编程 框架,比如 ReactiveCocoa、奥迪Q7x斯威夫特 或者PromiseKit。

实在,以后涉嫌「MVVM」你应该就会想到
ReactiveCocoa,反过来也是一律。即便大家得以经过简单的绑定来完成 MVVM
格局,不过 ReactiveCocoa(恐怕同类型的框架)会让您更大限度的去精晓MVVM。

响应式编程框架也有几许不佳的地点,能力越大权利越大嘛。用响应式编程用得不佳的话,很简单会把业务搞得一团糟。只怕这么说,假若有哪些地方出错了,你要求开销更加多的时光去调节。望着下边那张调用堆栈图感受一下:

在接下去的这么些小例子中,用响应式框架(F中华VF)或然 KVO
都展现有点大刀小用,所以大家用另一种办法:直接的调用 ViewModel 的
showGreeting 方法去创新本人(的 greeting 属性),(在 greeting
属性的 didSet 回调里面)用 greetingDidChange 闭包函数去立异 View
的显得。

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
    框架之中的 View 比 MVP 里面负责的事情要愈多一些。因为前端是经过
    ViewModel 的数据绑定来更新本身状态的,而后人只是把拥有的事件统统付给
    Presenter 去处理就完了,本身本身并不担当更新。
  • 可测性 – 因为 ViewModel 对 View
    是大惑不解的,那样大家对它的测试就变得很粗略。View
    应该也是可以被测试的,但是恐怕因为它对 UIKit的依靠,你会直接略过它。
  • 易用 – 在大家的例子里面,它的代码量基本跟 MVP
    持平,可是在实质上的运用当中 MVVM 会更精简一些。因为在 MVP
    下您不只怕不要把 View 的兼具事件都提交 Presenter
    去处理,而且须要手动的去立异 View 的场馆;而在 MVVM
    下,你只须要用绑定就足以化解。
    MVVM
    真的很有魔力,因为它不但结合了上述二种框架的长处,还不须要你为视图的换代去写额外的代码(因为在
    View 上早已做了多少绑定),此外它在可测性上的变现也仍旧很棒。

VIPE路虎极光 – 把搭建乐高积木的阅历运用到 iOS 应用的宏图上

VIPE昂科威 是我们最终3个要介绍的框架,这一个框架比较有意思的是它不属于其它一种
MV(X) 框架。

到近期截止,你可能认为大家把任务分开成三层,这么些颗粒度已经很不错了呢。以往VIPELacrosse 从另2个角度对任务举行了分割,这一次划分了 五层

  • Interactor(交互器)
    包蕴数据(Entities)可能互连网有关的事务逻辑。比如创建新的 entities
    恐怕从服务器上获取数据;要促成那几个作用,你可能会用到一些服务和保管(Services
    and Managers):那个或然会被误以为成是外部依赖东西,不过它们就是
    VIPE奥德赛 的 Interactor 模块。
  • Presenter(展示器) – 包含 UI(but UIKitindependent)相关的事体逻辑,可以调用 Interactor 中的方法。
  • Entities(实体) – 纯粹的数码对象。不包蕴数据访问层,因为那是
    Interactor 的天职。
  • Router(路由) – 负责 VIPE奥德赛 模块之间的转场

实在 VIPEPAJERO模块可以只是3个页面(screen),也可以是你使用里全数的用户采纳流程(the
whole user story)-
比如说「验证」那几个功能,它可以只是二个页面,也可以是接连相关的一组页面。你的每一种「乐高积木」想要有多大,都以你协调来支配的。

只要大家把 VIPE卡宴 和 MV(X)
系列做二个比较的话,大家会发觉它们在义务分开上面有上面的有个别分别:

  • Model(数据交互)的逻辑被转移到了 Interactor 里面,Entities
    只是3个怎样都毫无做的多少结构体。
  • Controller/Presenter/ViewModel 的义务里面,只有 UI
    的突显效果被更换来了 Presenter 里面。Presenter
    不持有直接改动数据的能力。
  • VIPER 是第①个把导航的职分单独划分出来的架构情势,负责导航的就是
    Router 层。

哪些科学的采纳导航(doing routing)对于 iOS
应用开发来说是二个挑衅,MV(X)
体系的架构完全就从不发现到(所以也不用处理)这么些题材。

下边的这些列子并不曾提到到导航和 VIPECR-V 模块间的转场,同样下边 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

咱俩再来评价下它在各方面的变现:

  • 划分 – 毫无疑问的,VIPE凯雷德 在职务分开方面是做的最好的。
  • 可测性 – 理所当然的,义务分开的越好,测试起来就越简单
  • 易用
    最终,你或然早就猜到了,上面两点便宜都以用维护性的代价换成的。三个很小的天职,大概就必要你为各品种写大批量的接口。

那就是说,大家到底应该给「乐高」3个怎么的评说呢?

一旦您在拔取 VIPE翼虎框架的时候有一种在用乐高积木搭建帝国大厦的痛感,那么您只怕正在犯错误;只怕对于你承担的拔取来说,还从未到应用 VIPE宝马X5的时候,你应有把部分业务考虑的再不难一些。总是有一对人忽略这么些标题,继续扛着大炮去打小鸟。我觉着大概是因为他们相信,即便目前来看维护费用高的不符常理,但是至少在后天他俩的接纳能够从
VIPEXC90 架构上赢得回报吧。若是你也跟她俩的视角一致的话,那本身提议你品尝一下
Generamba – 五个方可变动 VIPE中华V框架的工具。就算对于小编个人来讲,那感觉就如给大炮装上了一个活动瞄准系统,然后去做一件只用弹弓就能缓解的业务。

结论

咱俩大致理解了二种架构格局,对于那壹个让你疑忌的题材,作者期待您曾经找到了答案。不过一定,你应有早就意识到了,在选用架构情势这件难题方面,不设有啥茶绿子弹,你要求做的就是具体景况具体分析,权衡利弊而已。

故此在同3个应用里面,即使有三种混合的架构方式也是很正规的一件工作。比如:早先的时候,你用的是
MVC 架构,后来你意识到有3个破例的页面用 MVC
做的的话维护起来会一定的劳动;这些时候你可以只针对那1个页面用 MVVM
形式去支付,对于以前那些用 MVC
就能健康办事的页面,你完全没有要求去重构它们,因为三种架构是完全可以团结共处的。