iOS 架構(gòu)模式--解密 MVC拾因,MVP捏萍,MVVM以及VIPER架構(gòu)

http://www.cocoachina.com/articles/14916

作者:Bohdan Orlov

原文:iOS Architecture Patterns

為什么要關(guān)注架構(gòu)設(shè)計(jì)罢维?

因?yàn)榧偃缒悴魂P(guān)心架構(gòu)淹仑,那么總有一天,需要在同一個(gè)龐大的類中調(diào)試若干復(fù)雜的事情,你會(huì)發(fā)現(xiàn)在這樣的條件下匀借,根本不可能在這個(gè)類中快速的找到以及有效的修改任何bug.當(dāng)然颜阐,把這樣的一個(gè)類想象為一個(gè)整體是困難的,因此吓肋,有可能一些重要的細(xì)節(jié)總會(huì)在這個(gè)過程中會(huì)被忽略凳怨。如果現(xiàn)在的你正是處于這樣一個(gè)開發(fā)環(huán)境中,很有可能具體的情況就像下面這樣:

這個(gè)類是一個(gè)UIViewController的子類

數(shù)據(jù)直接在UIViewController中存儲(chǔ)

UIView類幾乎不做任何事情

Model 僅僅是一個(gè)數(shù)據(jù)結(jié)構(gòu)

單元測(cè)試覆蓋不了任何用例

以上這些情況仍舊會(huì)出現(xiàn)是鬼,即使是你遵循了Apple的指導(dǎo)原則并且實(shí)現(xiàn)了其 MVC(模式肤舞,所以,大可不必驚慌均蜜。Apple所提出的 MVC 模式存在一些問題李剖,我們之后會(huì)詳述。

在此囤耳,我們可以定義一個(gè)好的架構(gòu)應(yīng)該具備的特點(diǎn):

任務(wù)均衡分?jǐn)偨o具有清晰角色的實(shí)體

可測(cè)試性通常都來自與上一條(對(duì)于一個(gè)合適的架構(gòu)是非常容易)

易用性和低成本維護(hù)

為什么采用分布式?

采用分布式可以在我們要弄清楚一些事情的原理時(shí)保持一個(gè)均衡的負(fù)載篙顺。如果你認(rèn)為你的開發(fā)工作越多,你的大腦越能習(xí)慣復(fù)雜的思維充择,其實(shí)這是對(duì)的慰安。但是,不能忽略的一個(gè)事實(shí)是聪铺,這種思維能力并不是線性增長(zhǎng)的,而且也并不能很快的到達(dá)峰值萄窜。所以铃剔,能夠戰(zhàn)勝這種復(fù)雜性的最簡(jiǎn)單的方法就是在遵循 單一功能原則 的前提下,將功能劃分給不同的實(shí)體查刻。

為什么需要易測(cè)性键兜?

其實(shí)這條要求對(duì)于哪些習(xí)慣了單元測(cè)試的人并不是一個(gè)問題,因?yàn)樵谔砑恿诵碌奶匦曰蛘咭黾右恍╊惖膹?fù)雜性之后通常會(huì)失效穗泵。這就意味著普气,測(cè)試可以避免開發(fā)者在運(yùn)行時(shí)才發(fā)現(xiàn)問題----當(dāng)應(yīng)用到達(dá)用戶的設(shè)備,每一次維護(hù)都需要浪費(fèi)長(zhǎng)達(dá)至少一周的時(shí)間才能再次分發(fā)給用戶佃延。

為什么需要易用性现诀?

這個(gè)問題沒有固定的答案,但值得一提的是履肃,最好的代碼是那些從未寫過的代碼仔沿。因此,代碼寫的越少尺棋,Bug就越少封锉。這意味著希望寫更少的代碼不應(yīng)該被單純的解釋為開發(fā)者的懶惰,而且也不應(yīng)該因?yàn)槠珢鄹斆鞯慕鉀Q方案而忽視了它的維護(hù)開銷。

MV(X)系列概要

當(dāng)今我們已經(jīng)有很架構(gòu)設(shè)計(jì)模式方面的選擇:

![Uploading 1452152223816977_102409.png . . .]

MVC

MVP

MVVM

VIPER

前三種設(shè)計(jì)模式都把一個(gè)應(yīng)用中的實(shí)體分為以下三類:

Models--負(fù)責(zé)主要的數(shù)據(jù)或者操作數(shù)據(jù)的數(shù)據(jù)訪問層成福,可以想象 Perspn 和 PersonDataProvider 類碾局。

Views--負(fù)責(zé)展示層(GUI),對(duì)于iOS環(huán)境可以聯(lián)想一下以 UI 開頭的所有類奴艾。

Controller/Presenter/ViewModel--負(fù)責(zé)協(xié)調(diào) Model 和 View净当,通常根據(jù)用戶在View上的動(dòng)作在Model上作出對(duì)應(yīng)的更改,同時(shí)將更改的信息返回到View上握侧。

將實(shí)體進(jìn)行劃分給我們帶來了以下好處:

更好的理解它們之間的關(guān)系

復(fù)用(尤其是對(duì)于View和Model)

獨(dú)立的測(cè)試

讓我們開始了解MV(X)系列蚯瞧,之后再返回到VIPER模式。

MVC的過去

在我們探討Apple的MVC模式之前品擎,我們來看下傳統(tǒng)的MVC模式埋合。

傳統(tǒng)的MVC

在這里,View并沒有任何界限萄传,僅僅是簡(jiǎn)單的在Controller中呈現(xiàn)出Model的變化甚颂。想象一下,就像網(wǎng)頁一樣秀菱,在點(diǎn)擊了跳轉(zhuǎn)到某個(gè)其他頁面的連接之后就會(huì)完全的重新加載頁面振诬。盡管在iOS平臺(tái)上實(shí)現(xiàn)這這種MVC模式是沒有任何難度的,但是它并不會(huì)為我們解決架構(gòu)問題帶來任何裨益衍菱。因?yàn)樗旧硪彩歉厦矗齻€(gè)實(shí)體間相互都有通信,而且是緊密耦合的脊串。這很顯然會(huì)大大降低了三者的復(fù)用性辫呻,而這正是我們不愿意看到的。鑒于此我們不再給出例子琼锋。

“傳統(tǒng)的MVC架構(gòu)不適用于當(dāng)下的iOS開發(fā)”

蘋果推薦的MVC--愿景

Cocoa MVC

由于Controller是一個(gè)介于View 和 Model之間的協(xié)調(diào)器放闺,所以View和Model之間沒有任何直接的聯(lián)系。Controller是一個(gè)最小可重用單元缕坎,這對(duì)我們來說是一個(gè)好消息,因?yàn)槲覀兛傄乙粋€(gè)地方來寫邏輯復(fù)雜度較高的代碼谜叹,而這些代碼又不適合放在Model中匾寝。

理論上來講,這種模式看起來非常直觀叉谜,但你有沒有感到哪里有一絲詭異旗吁?你甚至聽說過,有人將MVC的縮寫展開成(Massive View Controller)停局,更有甚者很钓,為View controller減負(fù)也成為iOS開發(fā)者面臨的一個(gè)重要話題香府。如果蘋果繼承并且對(duì)MVC模式有一些進(jìn)展,所有這些為什么還會(huì)發(fā)生码倦?

蘋果推薦的MVC--事實(shí)

Realistic Cocoa MVC

Cocoa的MVC模式驅(qū)使人們寫出臃腫的視圖控制器企孩,因?yàn)樗鼈兘?jīng)常被混雜到View的生命周期中,因此很難說View和ViewController是分離的袁稽。盡管仍可以將業(yè)務(wù)邏輯和數(shù)據(jù)轉(zhuǎn)換到Model勿璃,但是大多數(shù)情況下當(dāng)需要為View減負(fù)的時(shí)候我們卻無能為力了,View的最大的任務(wù)就是向Controller傳遞用戶動(dòng)作事件推汽。ViewController最終會(huì)承擔(dān)一切代理和數(shù)據(jù)源的職責(zé)补疑,還負(fù)責(zé)一些分發(fā)和取消網(wǎng)絡(luò)請(qǐng)求以及一些其他的任務(wù),因此它的名字的由來...你懂的歹撒。

你可能會(huì)看見過很多次這樣的代碼:

varuserCell=tableView.dequeueReusableCellWithIdentifier("identifier")asUserCelluserCell.configureWithUser(user)

這個(gè)cell,正是由View直接來調(diào)用Model莲组,所以事實(shí)上MVC的原則已經(jīng)違背了,但是這種情況是一直發(fā)生的甚至于人們不覺得這里有哪些不對(duì)暖夭。如果嚴(yán)格遵守MVC的話锹杈,你會(huì)把對(duì)cell的設(shè)置放在 Controller 中,不向View傳遞一個(gè)Model對(duì)象迈着,這樣就會(huì)大大增加Controller的體積竭望。

“Cocoa 的MVC被寫成Massive View Controller 是不無道理的≡2ぃ”

直到進(jìn)行單元測(cè)試的時(shí)候才會(huì)發(fā)現(xiàn)問題越來越明顯咬清。因?yàn)槟愕腣iewController和View是緊密耦合的,對(duì)它們進(jìn)行測(cè)試就顯得很艱難--你得有足夠的創(chuàng)造性來模擬View和它們的生命周期奴潘,在以這樣的方式來寫View Controller的同時(shí)枫振,業(yè)務(wù)邏輯的代碼也逐漸被分散到View的布局代碼中去。

我們看下一些簡(jiǎn)單的例子:

importUIKitstructPerson{// ModelletfirstName:StringletlastName:String}classGreetingViewController:UIViewController{// View + Controllervarperson:Person!letshowGreetingButton=UIButton()letgreetingLabel=UILabel()overridefuncviewDidLoad(){super.viewDidLoad()self.showGreetingButton.addTarget(self,action:"didTapButton:",forControlEvents:.TouchUpInside)}funcdidTapButton(button:UIButton){letgreeting="Hello"+" "+self.person.firstName+" "+self.person.lastNameself.greetingLabel.text=greeting}// layout code goes here}// Assembling of MVCletmodel=Person(firstName:"David",lastName:"Blaine")letview=GreetingViewController()view.person=model;

“MVC可以在一個(gè)正在顯示的ViewController中實(shí)現(xiàn)”

這段代碼看起來可測(cè)試性并不強(qiáng)萤彩,我們可以把和greeting相關(guān)的都放到GreetingModel中然后分開測(cè)試,但是這樣我們就無法通過直接調(diào)用在GreetingViewController中的UIView的方法(viewDidLoad和didTapButton方法)來測(cè)試頁面的展示邏輯了斧拍,因?yàn)橐坏┱{(diào)用則會(huì)使整個(gè)頁面都變化雀扶,這對(duì)單元測(cè)試來講并不是什么好消息。

事實(shí)上肆汹,在單獨(dú)一個(gè)模擬器中(比如iPhone 4S)加載并測(cè)試UIView并不能保證在其他設(shè)備中也能正常工作愚墓,因此我建議在單元測(cè)試的Target的設(shè)置下移除"Host Application"項(xiàng),并且不要在模擬器中測(cè)試你的應(yīng)用昂勉。

“View和Controller的接口并不適合單元測(cè)試浪册。”

以上所述岗照,似乎Cocoa MVC 看起來是一個(gè)相當(dāng)差的架構(gòu)方案村象。我們來重新評(píng)估一下文章開頭我們提出的MVC一系列的特征:

任務(wù)均攤--View和Model確實(shí)是分開的笆环,但是View和Controller卻是緊密耦合的

可測(cè)試性--由于糟糕的分散性,只能對(duì)Model進(jìn)行測(cè)試

易用性--與其他幾種模式相比最小的代碼量厚者。熟悉的人很多躁劣,因而即使對(duì)于經(jīng)驗(yàn)不那么豐富的開發(fā)者來講維護(hù)起來也較為容易。

如果你不想在架構(gòu)選擇上投入更多精力库菲,那么Cocoa MVC無疑是最好的方案账忘,而且你會(huì)發(fā)現(xiàn)一些其他維護(hù)成本較高的模式對(duì)于你所開發(fā)的小的應(yīng)用是一個(gè)致命的打擊。

“就開發(fā)速度而言熙宇,Cocoa MVC是最好的架構(gòu)選擇方案鳖擒。”

MVP 實(shí)現(xiàn)了Cocoa的MVC的愿景

Passive View variant of MVP

這看起來不正是蘋果所提出的MVC方案嗎烫止?確實(shí)是的蒋荚,這種模式的名字叫做MVC,但是烈拒,這就是說蘋果的MVC實(shí)際上就是MVP了圆裕?不,并不是這樣的荆几。如果你仔細(xì)回憶一下吓妆,View是和Controller緊密耦合的,但是MVP的協(xié)調(diào)器Presenter并沒有對(duì)ViewController的生命周期做任何改變吨铸,因此View可以很容易的被模擬出來行拢。在Presenter中根本沒有和布局有關(guān)的代碼,但是它卻負(fù)責(zé)更新View的數(shù)據(jù)和狀態(tài)诞吱。

但是舟奠,“假如告訴你UIViewController就是View呢?”

就MVP而言房维,UIViewController的子類實(shí)際上就是Views并不是Presenters沼瘫。這點(diǎn)區(qū)別使得這種模式的可測(cè)試性得到了極大的提高,付出的代價(jià)是開發(fā)速度的一些降低咙俩,因?yàn)楸仨氁鲆恍┦謩?dòng)的數(shù)據(jù)和事件綁定耿戚,從下例中可以看出:

importUIKitstructPerson{// ModelletfirstName:StringletlastName:String}protocolGreetingView:class{funcsetGreeting(greeting:String)}protocolGreetingViewPresenter{init(view:GreetingView,person:Person)funcshowGreeting()}classGreetingPresenter:GreetingViewPresenter{unownedletview:GreetingViewletperson:Personrequiredinit(view:GreetingView,person:Person){self.view=viewself.person=person}funcshowGreeting(){letgreeting="Hello"+" "+self.person.firstName+" "+self.person.lastNameself.view.setGreeting(greeting)}}classGreetingViewController:UIViewController,GreetingView{varpresenter:GreetingViewPresenter!letshowGreetingButton=UIButton()letgreetingLabel=UILabel()overridefuncviewDidLoad(){super.viewDidLoad()self.showGreetingButton.addTarget(self,action:"didTapButton:",forControlEvents:.TouchUpInside)}funcdidTapButton(button:UIButton){self.presenter.showGreeting()}funcsetGreeting(greeting:String){self.greetingLabel.text=greeting}// layout code goes here}// Assembling of MVPletmodel=Person(firstName:"David",lastName:"Blaine")letview=GreetingViewController()letpresenter=GreetingPresenter(view:view,person:model)view.presenter=presenter

關(guān)于整合問題的重要說明

MVP是第一個(gè)如何協(xié)調(diào)整合三個(gè)實(shí)際上分離的層次的架構(gòu)模式,既然我們不希望View涉及到Model阿趁,那么在顯示的View Controller(其實(shí)就是View)中處理這種協(xié)調(diào)的邏輯就是不正確的膜蛔,因此我們需要在其他地方來做這些事情。例如脖阵,我們可以做基于整個(gè)App范圍內(nèi)的路由服務(wù)皂股,由它來負(fù)責(zé)執(zhí)行協(xié)調(diào)任務(wù),以及View到View的展示命黔。這個(gè)出現(xiàn)并且必須處理的問題不僅僅是在MVP模式中呜呐,同時(shí)也存在于以下集中方案中就斤。

我們來看下MVP模式下的三個(gè)特性的分析:

任務(wù)均攤--我們將最主要的任務(wù)劃分到Presenter和Model,而View的功能較少(雖然上述例子中Model的任務(wù)也并不多)卵史。

可測(cè)試性--非常好战转,由于一個(gè)功能簡(jiǎn)單的View層,所以測(cè)試大多數(shù)業(yè)務(wù)邏輯也變得簡(jiǎn)單

易用性--在我們上邊不切實(shí)際的簡(jiǎn)單的例子中以躯,代碼量是MVC模式的2倍槐秧,但同時(shí)MVP的概念卻非常清晰

“iOS 中的MVP意味著可測(cè)試性強(qiáng)、代碼量大忧设〉蟊辏”

MVP--綁定和信號(hào)

還有一些其他形態(tài)的MVP--監(jiān)控控制器的MVP。

這個(gè)變體包含了View和Model之間的直接綁定址晕,但是Presenter仍然來管理來自View的動(dòng)作事件膀懈,同時(shí)也能勝任對(duì)View的更新。

Supervising Presenter variant of the MVP

但是我們之前就了解到谨垃,模糊的職責(zé)劃分是非常糟糕的启搂,更何況將View和Model緊密的聯(lián)系起來。這和Cocoa的桌面開發(fā)的原理有些相似刘陶。

和傳統(tǒng)的MVC一樣胳赌,寫這樣的例子沒有什么價(jià)值,故不再給出匙隔。

MVVM--最新且是最偉大的MV(X)系列的一員

MVVM架構(gòu)是MV(X)系列最新的一員疑苫,因此讓我們希望它已經(jīng)考慮到MV(X)系列中之前已經(jīng)出現(xiàn)的問題。

從理論層面來講MVVM看起來不錯(cuò)纷责,我們已經(jīng)非常熟悉View和Model捍掺,以及Meditor,在MVVM中它是View Model再膳。

MVVM

它和MVP模式看起來非常像:

MVVM將ViewController視作View

在View和Model之間沒有緊密的聯(lián)系

此外挺勿,它還有像監(jiān)管版本的MVP那樣的綁定功能,但這個(gè)綁定不是在View和Model之間而是在View和ViewModel之間喂柒。

那么問題來了满钟,在iOS中ViewModel實(shí)際上代表什么?它基本上就是UIKit下的每個(gè)控件以及控件的狀態(tài)胳喷。ViewModel調(diào)用會(huì)改變Model同時(shí)會(huì)將Model的改變更新到自身并且因?yàn)槲覀兘壎薞iew和ViewModel,第一步就是相應(yīng)的更新狀態(tài)夭织。

綁定

我在MVP部分已經(jīng)提到這點(diǎn)了吭露,但是該部分我們?nèi)詴?huì)繼續(xù)討論。

如果我們自己不想自己實(shí)現(xiàn)尊惰,那么我們有兩種選擇:

基于KVO的綁定庫如 RZDataBinding 和 SwiftBond

完全的函數(shù)響應(yīng)式編程讲竿,比如像ReactiveCocoa泥兰、RxSwift或者 PromiseKit

事實(shí)上,尤其是最近题禀,你聽到MVVM就會(huì)想到ReactiveCoca鞋诗,反之亦然。盡管通過簡(jiǎn)單的綁定來使用MVVM是可實(shí)現(xiàn)的迈嘹,但是ReactiveCocoa卻能更好的發(fā)揮MVVM的特點(diǎn)削彬。

但是關(guān)于這個(gè)框架有一個(gè)不得不說的事實(shí):強(qiáng)大的能力來自于巨大的責(zé)任。當(dāng)你開始使用Reactive的時(shí)候有很大的可能就會(huì)把事情搞砸秀仲。換句話來說就是融痛,如果發(fā)現(xiàn)了一些錯(cuò)誤,調(diào)試出這個(gè)bug可能會(huì)花費(fèi)大量的時(shí)間神僵,看下函數(shù)調(diào)用棧:

Reactive Debugging

在我們簡(jiǎn)單的例子中雁刷,F(xiàn)RF框架和KVO被過渡禁用,取而代之地我們直接去調(diào)用showGreeting方法更新ViewModel保礼,以及通過greetingDidChange 回調(diào)函數(shù)使用屬性沛励。

importUIKitstructPerson{// ModelletfirstName:StringletlastName:String}protocolGreetingViewModelProtocol:class{vargreeting:String?{get}vargreetingDidChange:((GreetingViewModelProtocol)->())?{getset}// function to call when greeting did changeinit(person:Person)funcshowGreeting()}classGreetingViewModel:GreetingViewModelProtocol{letperson:Personvargreeting:String?{didSet{self.greetingDidChange?(self)}}vargreetingDidChange:((GreetingViewModelProtocol)->())?requiredinit(person:Person){self.person=person}funcshowGreeting(){self.greeting="Hello"+" "+self.person.firstName+" "+self.person.lastName}}classGreetingViewController:UIViewController{varviewModel:GreetingViewModelProtocol!{didSet{self.viewModel.greetingDidChange={[unownedself]viewModelinself.greetingLabel.text=viewModel.greeting}}}letshowGreetingButton=UIButton()letgreetingLabel=UILabel()overridefuncviewDidLoad(){super.viewDidLoad()self.showGreetingButton.addTarget(self.viewModel,action:"showGreeting",forControlEvents:.TouchUpInside)}// layout code goes here}// Assembling of MVVMletmodel=Person(firstName:"David",lastName:"Blaine")letviewModel=GreetingViewModel(person:model)letview=GreetingViewController()view.viewModel=viewModel

讓我們?cè)賮砜纯搓P(guān)于三個(gè)特性的評(píng)估:

任務(wù)均攤 -- 在例子中并不是很清晰,但是事實(shí)上炮障,MVVM的View要比MVP中的View承擔(dān)的責(zé)任多目派。因?yàn)榍罢咄ㄟ^ViewModel的設(shè)置綁定來更新狀態(tài),而后者只監(jiān)聽Presenter的事件但并不會(huì)對(duì)自己有什么更新铝阐。

可測(cè)試性 -- ViewModel不知道關(guān)于View的任何事情址貌,這允許我們可以輕易的測(cè)試ViewModel。同時(shí)View也可以被測(cè)試徘键,但是由于屬于UIKit的范疇练对,對(duì)他們的測(cè)試通常會(huì)被忽略。

易用性 -- 在我們例子中的代碼量和MVP的差不多吹害,但是在實(shí)際開發(fā)中螟凭,我們必須把View中的事件指向Presenter并且手動(dòng)的來更新View,如果使用綁定的話它呀,MVVM代碼量將會(huì)小的多螺男。

“MVVM很誘人,因?yàn)樗狭松鲜龇椒ǖ膬?yōu)點(diǎn)纵穿,并且由于在View層的綁定下隧,它并不需要其他附加的代碼來更新View,盡管這樣谓媒,可測(cè)試性依然很強(qiáng)淆院。”

VIPER--把LEGO建筑經(jīng)驗(yàn)遷移到iOS app的設(shè)計(jì)

VIPER是我們最后要介紹的句惯,由于不是來自于MV(X)系列土辩,它具備一定的趣味性支救。

迄今為止,劃分責(zé)任的粒度是很好的選擇拷淘。VIPER在責(zé)任劃分層面進(jìn)行了迭代各墨,VIPER分為五個(gè)層次:

VIPER

交互器 -- 包括關(guān)于數(shù)據(jù)和網(wǎng)絡(luò)請(qǐng)求的業(yè)務(wù)邏輯,例如創(chuàng)建一個(gè)實(shí)體(數(shù)據(jù))启涯,或者從服務(wù)器中獲取一些數(shù)據(jù)贬堵。為了實(shí)現(xiàn)這些功能,需要使用服務(wù)逝嚎、管理器扁瓢,但是他們并不被認(rèn)為是VIPER架構(gòu)內(nèi)的模塊,而是外部依賴补君。

展示器 -- 包含UI層面的業(yè)務(wù)邏輯以及在交互器層面的方法調(diào)用引几。

實(shí)體 -- 普通的數(shù)據(jù)對(duì)象,不屬于數(shù)據(jù)訪問層次挽铁,因?yàn)閿?shù)據(jù)訪問屬于交互器的職責(zé)伟桅。

路由器 -- 用來連接VIPER的各個(gè)模塊。

基本上叽掘,VIPER模塊可以是一個(gè)屏幕或者用戶使用應(yīng)用的整個(gè)過程--想想認(rèn)證過程楣铁,可以由一屏完成或者需要幾步才能完成,你的模塊期望是多大的更扁,這取決于你盖腕。

當(dāng)我們把VIPER和MV(X)系列作比較時(shí),我們會(huì)在任務(wù)均攤性方面發(fā)現(xiàn)一些不同:

Model 邏輯通過把實(shí)體作為最小的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換到交互器中浓镜。

Controller/Presenter/ViewModel的UI展示方面的職責(zé)移到了Presenter中溃列,但是并沒有數(shù)據(jù)轉(zhuǎn)換相關(guān)的操作。

VIPER是第一個(gè)通過路由器實(shí)現(xiàn)明確的地址導(dǎo)航模式膛薛。

“找到一個(gè)適合的方法來實(shí)現(xiàn)路由對(duì)于iOS應(yīng)用是一個(gè)挑戰(zhàn)听隐,MV(X)系列避開了這個(gè)問題『遄模”

例子中并不包含路由和模塊之間的交互雅任,所以和MV(X)系列部分架構(gòu)一樣不再給出例子。

importUIKitstructPerson{// Entity (usually more complex e.g. NSManagedObject)letfirstName:StringletlastName:String}structGreetingData{// Transport data structure (not Entity)letgreeting:Stringletsubject:String}protocolGreetingProvider{funcprovideGreetingData()}protocolGreetingOutput:class{funcreceiveGreetingData(greetingData:GreetingData)}classGreetingInteractor:GreetingProvider{weakvaroutput:GreetingOutput!funcprovideGreetingData(){letperson=Person(firstName:"David",lastName:"Blaine")// usually comes from data access layerletsubject=person.firstName+" "+person.lastNameletgreeting=GreetingData(greeting:"Hello",subject:subject)self.output.receiveGreetingData(greeting)}}protocolGreetingViewEventHandler{funcdidTapShowGreetingButton()}protocolGreetingView:class{funcsetGreeting(greeting:String)}classGreetingPresenter:GreetingOutput,GreetingViewEventHandler{weakvarview:GreetingView!vargreetingProvider:GreetingProvider!funcdidTapShowGreetingButton(){self.greetingProvider.provideGreetingData()}funcreceiveGreetingData(greetingData:GreetingData){letgreeting=greetingData.greeting+" "+greetingData.subjectself.view.setGreeting(greeting)}}classGreetingViewController:UIViewController,GreetingView{vareventHandler:GreetingViewEventHandler!letshowGreetingButton=UIButton()letgreetingLabel=UILabel()overridefuncviewDidLoad(){super.viewDidLoad()self.showGreetingButton.addTarget(self,action:"didTapButton:",forControlEvents:.TouchUpInside)}funcdidTapButton(button:UIButton){self.eventHandler.didTapShowGreetingButton()}funcsetGreeting(greeting:String){self.greetingLabel.text=greeting}// layout code goes here}// Assembling of VIPER module, without Routerletview=GreetingViewController()letpresenter=GreetingPresenter()letinteractor=GreetingInteractor()view.eventHandler=presenterpresenter.view=viewpresenter.greetingProvider=interactorinteractor.output=presenter

讓我們?cè)賮碓u(píng)估一下特性:

任務(wù)均攤 -- 毫無疑問咨跌,VIPER是任務(wù)劃分中的佼佼者沪么。

可測(cè)試性 -- 不出意外地,更好的分布性就有更好的可測(cè)試性锌半。

易用性 -- 最后你可能已經(jīng)猜到了維護(hù)成本方面的問題禽车。你必須為很小功能的類寫出大量的接口。

什么是LEGO

當(dāng)使用VIPER時(shí),你的感覺就像是用樂高積木來搭建一個(gè)城堡哭当,這也是一個(gè)表明當(dāng)前存在一些問題的信號(hào)∪叱海可能現(xiàn)在就應(yīng)用VIPER架構(gòu)還為時(shí)過早钦勘,考慮一些更為簡(jiǎn)單的模式可能會(huì)更好。一些人會(huì)忽略這些問題亚亲,大材小用彻采。假定他們篤信VIPER架構(gòu)會(huì)在未來給他們的應(yīng)用帶來一些好處,雖然現(xiàn)在維護(hù)起來確實(shí)是有些不合理捌归。如果你也持這樣的觀點(diǎn)肛响,我為你推薦 Generamba 這個(gè)用來搭建VIPER架構(gòu)的工具。雖然我個(gè)人感覺惜索,使用起來就像加農(nóng)炮的自動(dòng)瞄準(zhǔn)系統(tǒng)特笋,而不是簡(jiǎn)單的像投石器那樣的簡(jiǎn)單的拋擲。

總結(jié)

我們了解了集中架構(gòu)模式巾兆,希望你已經(jīng)找到了到底是什么在困擾你猎物。毫無疑問通過閱讀本篇文章,你已經(jīng)了解到其實(shí)并沒有完全的銀彈角塑。所以選擇架構(gòu)是一個(gè)根據(jù)實(shí)際情況具體分析利弊的過程蔫磨。

因此,在同一個(gè)應(yīng)用中包含著多種架構(gòu)圃伶。比如堤如,你開始的時(shí)候使用MVC,然后突然意識(shí)到一個(gè)頁面在MVC模式下的變得越來越難以維護(hù)窒朋,然后就切換到MVVM架構(gòu)搀罢,但是僅僅針對(duì)這一個(gè)頁面。并沒有必要對(duì)哪些MVC模式下運(yùn)轉(zhuǎn)良好的頁面進(jìn)行重構(gòu)炼邀,因?yàn)槎呤强梢圆⒋娴摹?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末魄揉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拭宁,更是在濱河造成了極大的恐慌洛退,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杰标,死亡現(xiàn)場(chǎng)離奇詭異兵怯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)腔剂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門媒区,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事袜漩⌒靼郑” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵宙攻,是天一觀的道長(zhǎng)奠货。 經(jīng)常有香客問我,道長(zhǎng)座掘,這世上最難降的妖魔是什么递惋? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮溢陪,結(jié)果婚禮上萍虽,老公的妹妹穿的比我還像新娘。我一直安慰自己形真,他們只是感情好杉编,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著没酣,像睡著了一般王财。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上裕便,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天绒净,我揣著相機(jī)與錄音,去河邊找鬼偿衰。 笑死挂疆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的下翎。 我是一名探鬼主播缤言,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼视事!你這毒婦竟也來了胆萧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤俐东,失蹤者是張志新(化名)和其女友劉穎跌穗,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虏辫,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蚌吸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了砌庄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羹唠。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奕枢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出佩微,到底是詐尸還是另有隱情缝彬,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布哺眯,位于F島的核電站跌造,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏族购。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一陵珍、第九天 我趴在偏房一處隱蔽的房頂上張望寝杖。 院中可真熱鬧,春花似錦互纯、人聲如沸瑟幕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽只盹。三九已至,卻和暖如春兔院,著一層夾襖步出監(jiān)牢的瞬間殖卑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工坊萝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留孵稽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓十偶,卻偏偏與公主長(zhǎng)得像菩鲜,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惦积,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355