新匍京视频在线MVC一定是我们熟识的一种架构格局,大家先简要回看一下脚下可比主流的架构情势

作为一个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?在此以前听说过
VIPER,但是又纠结是否值得去学?

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的分开,其实有一个胖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 而已
  • 易用
    相较于其余格局,它的代码量最少。而且基本上每个人都很熟谙它,即便是没太多经历的开发者也能维护。

对于设计方式的求学是一件不难上瘾的政工,所以先唤醒你瞬间:在你读完那篇作品之后,可能会比读从前有更加多的问号,比如:

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可以看这里

(MVC)何人来承担网络请求:是 Model 依旧 Controller?

局部表达:

MVP架构拥有八个实在独立的分层,所以在组装的时候会有部分题目,而MVP也成了首个披露那种题材的架构,因为大家不想让View知道Model的音信,所以在脚下的Controller去组装是不得法的,大家应有在其余的地点成功组建。比如大家可以创立一个应用层的Router服务,让它来承担组建和View-to-View的转场。这些问题下许多情势中都设有。

下边计算一下MVP的各方面表现:

  • 划分——我们把半数以上义务都分配到了Presenter和Model里面,而View基本不须要做什么
  • 可测性——大家得以因此View来测试大多数业务逻辑
  • 易用——代码量差不离是MVC架构的两倍,但是MVP的笔触仍旧蛮清晰的

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

(MVVM)我该怎么去把一个 Model 传递给一个新创制的 View 的 ViewModel?

MVVM

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

新匍京视频在线 7

MVVM

MVVM和MVP很像:

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

其余它还让VIew和ViewModel做了多少绑定。ViewModel可以调用对Model做变更,也足以再Model更新的时候对本身举行调整,然后经过View和ViewModel之间的绑定,对View举行相应的更新。

(VIPER)什么人来顶住创建 VIPER 模块:是 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的,需要总体的Demo请看这里

上面我们再来对MVVM的各地点表现做一个讲评:

  • 划分——MVVM 框架之中的 View 比 MVP
    里面负责的工作要越多一些。因为前者是通过 ViewModel
    的数目绑定来更新自己意况的,而后者只是把具有的风浪统统付给 Presenter
    去处理就完了,自己我并不担当更新。
  • 可测性—— 因为 ViewModel 对 View
    是不解的,那样大家对它的测试就变得很粗略。View
    应该也是能够被测试的,但是或许因为它对 UI基特的着重性,你会直接略过它。
  • 易用——它比MVP会越发简明,因为在 MVP 下您必要求把 View
    的所有事件都交由 Presenter 去处理,而且亟需手动的去立异 View
    的境况;而在 MVVM 下,你只须要用绑定就足以缓解。

综上:MVVM
真的很有魅力,因为它不但结合了上述三种框架的长处,还不须要你为视图的翻新去写额外的代码(因为在
View 上早已做了多少绑定),其余它在可测性上的变现也照例很棒。

为了不难易懂,以上的Demo都相当简短,不明了看了这篇博客能不能加深你对MV(X)的一些通晓,那一个驾驭也仅看成自己个人的部分参阅,有哪些难堪的地点希望大家提出。


怎么要在意架构的取舍呢?

因为只要您忽视的话,难保一天,你就须求去调节一个高大无比又怀有各样题材的类,然后你会发觉在这几个类里面,你一点一滴就找不到也修复不了任何
bug。一般的话,把如此大的一个类作为完整放在脑子里记着是一件万分不便的事情,你总是难免会忘掉一些相比根本的细节。如若您发觉在您的运用里面早已初始现出那种景色了,那你很可能遇见过上面那类问题:

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

实则就是你根据了 Apple 的设计规范,达成了 Apple 的 MVC
框架,也仍然一如既往会遇见上面这一个问题;所以也没怎么好悲伤的。Apple 的 MVC
框架 有它自身的瑕疵,然而那一个我们前边再说。

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

  1. 用严厉定义的角色,平衡的将职分 划分 给区其他实业。
  2. 可测性
    经常取决于上边说的率先点(不用太操心,假如架构曾几何时的话,做到那一点并不难)。
  3. 易用 并且保养开销低。

怎么要分开?

当我们准备去领略事物的干活原理的时候,划分可以减轻大家的头颅压力。如若你认为开发的越来越多,大脑就越能适应去处理复杂的行事,确实是如此。然而大脑的这种力量不是线性升高的,而且很快就会达到一个瓶颈。所以要处理千丝万缕的政工,最好的点子照旧在按照单一权利原则 的口径下,将它的天职责开到多少个实体中去。

缘何要可测性?

对此那一个对单元测试心存感激的人的话,应该不会有那上头的疑云:单元测试辅助她们测试出了新功用里面的错误,或者是帮他们找出了重构的一个复杂类里面的
bug。那象征这几个单元测试支持那么些开发者们在程序运行在此以前就意识了问题,这么些问题即便被忽视的话很可能会提交到用户的设备上去;而修复那个题目,又至少须求七天左右的时刻(AppStore
审核)。

为啥要易用

那块没什么好说的,直说一些:最好的代码是那几个从没被写出来的代码。代码写的越少,问题就越少;所以开发者想少写点代码并不一定就是因为她懒。还有,当您想用一个相比较聪明 的点子的时候,全完不要忽视了它的有限支撑费用。

MV(X) 的基本要素

现在我们面对架构设计方式的时候有了不少增选:

首先前三种情势都是把所有的实业归类到了上边三种分类中的一种:

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

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

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

让自己从 MV(X) 系列开端讲起,最终讲 VIPER。

MVC – 它原先的指南

在开始谈论 Apple 的 MVC
在此以前,大家先来看下传统的MVC

在那种架构下,View 是无状态的,在 Model 变化的时候它只是简单的被
Controller
重绘;就像网页一样,点击了一个新的链接,整个网页就再次加载。即使那种架构可以在
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
的体现并不可能担保在其他设备上(比如
华为平板)也能完美运转。所以我提出把「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)里面去开展组装肯定是不科学的,大家相应在别的的地点成功组建。比如,大家可以创造一个应用层(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) 里面最新的一个,让大家期望它在出现的时候曾经考虑到了 MV(X)
格局之前所遭遇的题目吗。

辩护上的话,Model – View – ViewModel 看起来万分棒。View 和 Model
大家曾经都熟谙了,中间人的角色咱们也如数家珍了,可是在那边中间人的角色变成了
ViewModel。

它跟 MVP 很像:

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

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

那就是说,iOS 里面的 ViewModel 到底是个怎么着事物吗?本质上来讲,他是单独于
UI基特(Kit) 的, View 和 View 的情况的一个呈现(representation)。ViewModel
能主动调用对 Model 做改变,也能在 Model
更新的时候对自我举行调整,然后经过 View 和 ViewModel 之间的绑定,对 View
也进行相应的创新。

绑定

本人在 MVP
的片段不难的提过那几个情节,在此地让我们再延长探究一下。绑定那几个定义来源于
OS X 平台的开发,不过在 iOS
平台上边,我们并不曾对应的开发工具。当然,大家也有 KVO 和
通知,可是用那几个措施去做绑定不太有利。

那么,倘诺大家不想自己去写他们来说,上面提供了三个挑选:

  • 选一个依照 KVO 的绑定库,比如 RZDataBinding 或者 斯维·夫特(Sw·ift)Bond。
  • 行使全量级的 函数式响应编程 框架,比如 ReactiveCocoa、Rx斯威·夫特(S·wift) 或者
    PromiseKit。

实在,现在事关「MVVM」你应有就会想到
ReactiveCocoa,反过来也是相同。尽管大家得以经过简单的绑定来兑现 MVVM
方式,可是 ReactiveCocoa(或者同类型的框架)会让您更大限度的去精通MVVM。

响应式编程框架也有几许糟糕的地点,能力越大权利越大嘛。用响应式编程用得不好的话,很简单会把工作搞得一团糟。或者这么说,倘使有哪些地点出错了,你须求开销越来越多的时日去调节。望着下边那张调用堆栈图感受一下:

在接下去的那几个小例子中,用响应式框架(FRF)或者 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 上一度做了数据绑定),别的它在可测性上的显示也一如既往很棒。

VIPER – 把搭建乐高积木的经验运用到 iOS 应用的统筹上

VIPER 是咱们最终一个要介绍的框架,那一个框架相比好玩的是它不属于其余一种
MV(X) 框架。

到近期甘休,你也许以为大家把职责分开成三层,那个颗粒度已经很科学了吧。现在
VIPER 从另一个角度对职务进行了分割,本次划分了 五层

  • Interactor(交互器)
    蕴含数据(Entities)或者网络有关的作业逻辑。比如创造新的 entities
    或者从服务器上获取数据;要促成那些功用,你也许会用到部分服务和管理(瑟维斯(Service)s
    and Managers):那个或许会被误以为成是表面爱护东西,不过它们就是
    VIPER 的 Interactor 模块。
  • Presenter(展示器) – 包含 UI(but UIKitindependent)相关的业务逻辑,可以调用 Interactor 中的方法。
  • Entities(实体) – 纯粹的数据对象。不包蕴数据访问层,因为那是
    Interactor 的任务。
  • Router(路由) – 负责 VIPER 模块之间的转场

实则 VIPER
模块可以只是一个页面(screen),也足以是您选拔里整套的用户使用流程(the
whole user story)-
比如说「验证」这么些作用,它可以只是一个页面,也足以是连连相关的一组页面。你的每个「乐高积木」想要有多大,都是你协调来决定的。

假如大家把 VIPER 和 MV(X)
连串做一个比较的话,大家会发现它们在任务分开上面有上面的一些区分:

  • Model(数据交互)的逻辑被转换来了 Interactor 里面,Entities
    只是一个什么样都不要做的数据结构体。
  • Controller/Presenter/ViewModel 的义务里面,惟有 UI
    的来得效果被撤换来了 Presenter 里面。Presenter
    不富有直接改动数据的能力。
  • VIPER 是第三个把导航的义务单独划分出来的架构格局,负责导航的就是
    Router 层。

怎么着正确的施用导航(doing routing)对于 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
框架的时候有一种在用乐高积木搭建帝国大厦的觉得,那么您或许
正在犯错误;可能对于你承担的行使来说,还平昔不到使用 VIPER
的时候,你应当把部分事情考虑的再不难一些。总是有一部分人不经意那么些题材,继续扛着大炮去打小鸟。我觉着说不定是因为她俩相信,即使眼下来看维护费用高的离题万里常理,不过起码在未来她们的运用可以从
VIPER 架构上获取回报吧。如若您也跟她俩的见地同样的话,那自己指出你品味一下
Generamba – 一个得以变动 VIPER
框架的工具。就算对于我个人来讲,那感觉似乎给大炮装上了一个活动瞄准系统,然后去做一件只用弹弓就能一蹴而就的工作。

结论

我们简要询问了两种架构情势,对于这个让您质疑的问题,我期望你早就找到了答案。可是毫无疑问,你应当已经发现到了,在挑选架构格局这件问题方面,不设有哪些
银色子弹,你需求做的就是具体景况具体分析,权衡利弊而已。

为此在同一个利用里面,就算有三种混合的架构形式也是很健康的一件业务。比如:开首的时候,你用的是
MVC 架构,后来你发觉到有一个例外的页面用 MVC
做的的话维护起来会一定的麻烦;那些时候你可以只针对这个页面用 MVVM
格局去付出,对于在此以前那么些用 MVC
就能健康干活的页面,你一点一滴没有需求去重构它们,因为二种架构是全然可以友善共处的。

相关文章