iOS架構(gòu)設(shè)計:揭秘MVC, MVP, MVVM以及VIPER

不要錯過最新的iOS開發(fā)技能樹 —— github地址

更新:在這里可以看到幻燈片
在iOS中使用MVC時感覺怪怪的势就?對切換到MVVM有疑慮最域?聽說過VIPER咒程,但不知道是否值得洋腮?

往下看箫柳,你將會找到這些問題的答案,如果還有疑問啥供,請在評論區(qū)留言悯恍。

你將了解到在iOS環(huán)境下如何進行系統(tǒng)架構(gòu)設(shè)計。我們將簡單回顧一些流行的框架伙狐,并通過實踐一些小例子來比較它們的理論涮毫。如果需要更多詳細信息,請參考文章中出現(xiàn)的鏈接贷屎。

掌握設(shè)計模式可能會讓人上癮罢防,所以要小心:你可能在閱讀這篇文章之前已經(jīng)問過自己一些問題,比如說:
誰應(yīng)該擁有聯(lián)網(wǎng)請求:Model還是Controller唉侄?
如何將Model傳遞到新View的View Model中咒吐?
誰創(chuàng)建了一個新的VIPER模塊:Router還是Presenter?

為什么要糾結(jié)選擇什么架構(gòu)呢属划?

假如有一天恬叹,你在調(diào)試一個實現(xiàn)了幾十種功能的龐大的類時,你會發(fā)現(xiàn)自己很難找到并修復(fù)你的類中的任何錯誤同眯。并且绽昼,很難把這個類作為一個整體來考慮,因此须蜗,你總會忽略一些重要的細節(jié)硅确。如果你的應(yīng)用程序中已經(jīng)出現(xiàn)了這種情況,那么很有可能:

  • 這類是UIViewController類明肮。
  • UIViewController直接存儲和處理你的數(shù)據(jù)
  • 你的UIView中幾乎沒有做任何事情
  • Model僅僅是一個數(shù)據(jù)結(jié)構(gòu)
  • 單元測試覆蓋不了任何內(nèi)容

即使你遵循了蘋果的指導(dǎo)方針并實現(xiàn)了蘋果的MVC模式菱农,這種情況還是會發(fā)生的,所以不要難過晤愧。蘋果的MVC有點問題大莫,這個我們稍后再談蛉腌。

讓我們定義一個優(yōu)秀系統(tǒng)結(jié)構(gòu)的特征:
1.角色間職責(zé)的清晰分配(分布式)官份。
2.可測試性通常來自第一個特性(不必擔(dān)心:使用適當(dāng)?shù)南到y(tǒng)結(jié)構(gòu)是很容易的)只厘。
3.使用方便,維護成本低舅巷。

為什么要采用分布式

當(dāng)我們想弄清楚某些事情是如何運作時羔味,采用分布式能讓我們的大腦思路清晰。如果你認為你開發(fā)越多钠右,你的大腦就越能理解復(fù)雜性赋元,那么你是對的。但這種能力不是線性的飒房,很快就會達到上限搁凸。因此,克服復(fù)雜性的最簡單方法是按照單一職責(zé)原則在多個實體之間劃分職責(zé)狠毯。

為什么要可測試

對于那些已經(jīng)習(xí)慣了單元測試的人來說护糖,這通常不是問題,因為在添加了新的特性或者要增加一些類的復(fù)雜性之后通常會失敗嚼松。這意味著測試能夠降低應(yīng)用程序在用戶的設(shè)備上發(fā)生問題的概率嫡良,那時修復(fù)也許需要一個星期(審核)才能到達用戶。

為什么要易用性

這并不需要回答献酗,但值得一提的是寝受,最好的代碼是從未編寫過的代碼。因此罕偎,你擁有的代碼越少很澄,你擁有的bug就越少。這意味著編寫更少代碼的愿望決不能僅僅由開發(fā)人員的懶惰來解釋颜及,你不應(yīng)該偏愛看起來更聰明的解決方案而忽視它的維護成本痴怨。

MV(X) 簡介

現(xiàn)在我們在架構(gòu)設(shè)計模式上有很多選擇:

他們中的三個假設(shè)將應(yīng)用程序的實體分成3類:

  • Models — 負責(zé)保存數(shù)據(jù)或數(shù)據(jù)訪問層,操縱數(shù)據(jù)器予,例如“人”或“提供數(shù)據(jù)的人”浪藻。
  • Views? — ?負責(zé)表示層(GUI),iOS環(huán)境下通常以“UI”前綴乾翔。
  • Controller/Presenter/ViewModel? — ?Model和View之間的中介爱葵,一般負責(zé)在用戶操作View時更新Model,以及當(dāng)Model變化時更新View反浓。

這種劃分能讓我們:

  • 更好地理解它們(如我們所知)
  • 重用它們(尤其是View和Model)
  • 獨立地進行測試(單元測試)

讓我們從MV(X)開始萌丈,稍后在回到VIPER:

MVC

曾經(jīng)

在討論蘋果對MVC的看法之前,讓我們先看看傳統(tǒng)的MVC雷则。

傳統(tǒng)的MVC

在上圖的情況下辆雾,View是無狀態(tài)的。一旦Model被改變月劈,Controller就會簡單地渲染它度迂。例如:網(wǎng)頁完全加載后藤乙,一旦你按下鏈接,就導(dǎo)航到其他地方惭墓。
雖然在iOS應(yīng)用用傳統(tǒng)的MVC架構(gòu)也可以實現(xiàn)坛梁,但這并沒有多大意義,由于架構(gòu)問題?——三個實體是緊耦合的腊凶,每個實體和其他兩個通信划咐。這大大降低了可重用性——這可不是你希望在你的應(yīng)用程序看到的。出于這個原因钧萍,我們甚至不想編寫規(guī)范的MVC示例褐缠。

傳統(tǒng)的MVC似乎不適用于現(xiàn)代IOS開發(fā)。

蘋果的MVC

愿景:
Cocoa MVC

Controller是View和Model之間的中介风瘦,這樣他們就解耦了送丰。最小的可重用單元是Controller,這對我們來說是個好消息弛秋,因為我們必須有一個來放那些不適合放入Model的復(fù)雜業(yè)務(wù)邏輯的地方器躏。
從理論上講,它看起來很簡單蟹略,但你覺得有些地方不對登失,對吧?你甚至聽到有人說MVC全稱應(yīng)該改為Massive View Controller(大量的視圖控制器)挖炬。此外揽浙,為View controller減負也成為iOS開發(fā)者面臨的一個重要話題。
如果蘋果只接受傳統(tǒng)的MVC并改進了它意敛,為什么會出現(xiàn)這種情況呢馅巷?

實際情況:
事實上的Cocoa MVC

Cocoa MVC鼓勵人們編寫大規(guī)模的視圖控制器,而且由于它們涉及View的生命周期草姻,所以很難說它們(View和Controller)是分離的钓猬。
雖然你仍有能力將一些業(yè)務(wù)邏輯和數(shù)據(jù)轉(zhuǎn)換成Model,但你沒辦法將View從Controller中分離撩独。在大多數(shù)時候所有View的責(zé)任是把事件傳遞給Controller敞曹。
ViewController最終演變成一個其他人的delegate和data source,通常負責(zé)分派和取消網(wǎng)絡(luò)請求…你明白的综膀。
你見過多少這樣的代碼澳迫?:

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

Cell(一個View)跟一個Model直接綁定了!所以MVC準則被違反了剧劝,但是這種情況總是發(fā)生橄登,通常人們不會覺得它是錯誤的。如果你嚴格遵循MVC,那么你應(yīng)該從Controller配置cell拢锹,而不是將Model傳遞到cell中谣妻,這將增大Controller。

Cocoa MVC 的全稱應(yīng)該是 Massive View Controller.

在單元測試之前面褐,這個問題可能并不明顯(希望在你的項目中是這樣)。
由于視圖控制器與視圖緊密耦合取胎,因此很難測試——因為在編寫視圖控制器的代碼時展哭,你必須模擬View的生命周期,從而使你的業(yè)務(wù)邏輯盡可能地與View層的代碼分隔開來闻蛀。
讓我們看一看簡單的操場例子:

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
        
    }
    // 這里寫布局代碼
}
// 組裝MVC
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
view.person = model;

MVC在可見的ViewController中進行組裝

這似乎不太容易測試匪傍,對嗎?
我們可以將greeting移動到新的GreetingModel類中并分別進行測試觉痛,但我們不能在不調(diào)用GreetingViewController的有關(guān)方法(viewDidLoad, didTapButton役衡,這將會加載所有的View) 的情況下測試UIView中的顯示邏輯(雖然在上面的例子中沒有太多這樣的邏輯)。這不利于單元測試薪棒。
事實上手蝎,在一個模擬器(如iPhone 4S)中測試UIViews并不能保證它會在其他設(shè)備良好的工作(例如iPad),所以我建議從你的單元測試Target中刪除“Host Application”選項俐芯,然后脫離應(yīng)用程序運行你的測試棵介。

View和Controller之間的交互在單元測試中是不可測試的。

如此看來吧史,Cocoa MVC 模式 似乎是一個很糟糕的選擇邮辽。但是讓我們根據(jù)文章開頭定義的特性來評估它:

  • 職責(zé)拆分 — View和Model實現(xiàn)了分離,但是View與Controller仍是緊耦合贸营。
  • 可測性 — 由于模式的原因吨述,你只能測試你的Model。
  • 易用性 — 相比于其他模式代碼量最少钞脂。此外揣云,每個人都熟悉它,即使經(jīng)驗不太豐富的開發(fā)人員也能夠維護它冰啃。

如果你不愿意在項目的架構(gòu)上投入太多的時間灵再,那么Cocoa MVC 就是你應(yīng)該選擇的模式。而且你會發(fā)現(xiàn)用其他維護成本較高的模式開發(fā)小的應(yīng)用是一個致命的錯誤亿笤。

Cocoa MVC是開發(fā)速度最快的架構(gòu)模式翎迁。

MVP

MVP 實現(xiàn)了Cocoa的MVC的愿景
Passive View 變體 — MVP

這看起來不正是蘋果的MVC嗎?是的净薛,它的名字是MVP(Passive View variant汪榔,被動視圖變體)。等等...這是不是意味著蘋果的MVC實際上是MVP?不痴腌,不是這樣雌团。如果你仔細回憶一下,View是和Controller緊密耦合的士聪,但是MVP的中介Presenter并沒有對ViewController的生命周期做任何改變锦援,因此View可以很容易的被模擬出來。在Presenter中根本沒有和布局有關(guān)的代碼剥悟,但是它卻負責(zé)更新View的數(shù)據(jù)和狀態(tài)灵寺。

假如告訴你,UIViewController就是View呢?

在MVP中区岗,UIViewController的子類實際上是Views而不是Presenters略板。這種模式的可測試性得到了極大的提高,付出的代價是開發(fā)速度的一些降低慈缔,因為必須要做一些手動的數(shù)據(jù)和事件綁定叮称,從下例中可以看出:

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
    }
    
    // 布局代碼
}
// 裝配 MVP
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
let presenter = GreetingPresenter(view: view, person: model)
view.presenter = presenter
裝配問題的重要說明

MVP是第一個揭示裝配問題的模式,因為它有三個獨立的層藐鹤。既然我們不希望View和Model耦合瓤檐,那么在顯示的View Controller(其實就是View)中處理這種協(xié)調(diào)的邏輯就是不正確的,因此我們需要在其他地方來做這些事情娱节。例如距帅,我們可以做基于整個App范圍內(nèi)的路由服務(wù),由它來負責(zé)執(zhí)行協(xié)調(diào)任務(wù)括堤,以及View到View的展示碌秸。這不僅僅是在MVP模式中必須處理的問題,同時也存在于以下集中方案中悄窃。

讓我們看看MVP的特點:

  • 職責(zé)拆分 — 我們將最主要的任務(wù)劃分到Presenter和Model讥电,而View的功能較少(雖然上述例子中Model的任務(wù)也并不多)。
  • 可測性 — 非常好轧抗,基于一個功能簡單的View層恩敌,可以測試大多數(shù)業(yè)務(wù)邏輯
  • 易用性 — 在我們上邊不切實際的簡單的例子中,代碼量是MVC模式的2倍横媚,但同時MVP的概念卻非常清晰纠炮。

iOS 中的MVP意味著可測試性強、代碼量大灯蝴。

MVP

關(guān)于Bindings和Hooters

還有一些其他形態(tài)的MVP —— Supervising Controller MVP(監(jiān)聽Controller的MVP)恢口。這個變體的變化包括View和Model之間的直接綁定,但是Presenter(Supervising Controller)仍然來管理來自View的動作事件穷躁,同時也能勝任對View的更新耕肩。

監(jiān)聽Controller的MVP

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

和傳統(tǒng)的MVC一樣,寫這樣的例子沒有什么價值梳虽,故不再給出址芯。

MVVM

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

MVVM架構(gòu)是MV(X)系列最新的成員,我們希望它已經(jīng)考慮到MV(X)系列中之前已經(jīng)出現(xiàn)的問題窜觉。
從理論層面來講Model-View-ViewModel看起來不錯谷炸,我們已經(jīng)非常熟悉View和Model,以及Meditor(中介)竖螃,在這里它叫做View Model淑廊。

MVVM

它和MVP模式看起來很像:

  • MVVM也將ViewController視作View
  • 在View和Model之間沒有耦合

此外逗余,它還有像Supervising版本的MVP那樣的綁定功能特咆,但這個綁定不是在View和Model之間而是在View和ViewModel之間。

那么在iOS中ViewModel到底代表了什么录粱?它基本上就是UIKit下的獨立控件以及控件的狀態(tài)腻格。ViewModel調(diào)用會改變Model同時會將Model的改變更新到自身并且因為我們綁定了View和ViewModel,第一步就是相應(yīng)的更新狀態(tài)啥繁。

綁定

我在MVP部分已經(jīng)提到這點了菜职,但是在這里我們來繼續(xù)討論。
綁定是從OS X開發(fā)中衍生出來的旗闽,但是我們沒有在iOS開發(fā)中使用它們酬核。當(dāng)然我們有KVO通知,但它們沒有綁定方便适室。
如果我們自己不想自己實現(xiàn)嫡意,那么我們有兩種選擇:

事實上,尤其是最近汽畴,你聽到MVVM就會想到ReactiveCoca旧巾,反之亦然。盡管通過簡單的綁定來使用MVVM是可實現(xiàn)的忍些,但是ReactiveCocoa(或其變體)卻能更好的發(fā)揮MVVM的特點鲁猩。

函數(shù)響應(yīng)式框架有一個殘酷的事實:強大的能力來自于巨大的責(zé)任。當(dāng)你開始使用Reactive的時候有很大的可能就會把事情搞砸罢坝。換句話來說就是绳匀,如果發(fā)現(xiàn)了一些錯誤,調(diào)試出這個bug可能會花費大量的時間,看下函數(shù)調(diào)用棧:


Reactive Debugging

在我們簡單的例子中疾棵,F(xiàn)RF框架和KVO被禁用戈钢,取而代之地我們直接去調(diào)用showGreeting方法更新ViewModel,以及通過greetingDidChange 回調(diào)函數(shù)使用屬性是尔。

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
}
// 裝配 MVVM
let model = Person(firstName: "David", lastName: "Blaine")
let viewModel = GreetingViewModel(person: model)
let view = GreetingViewController()
view.viewModel = viewModel

讓我們再來看看關(guān)于三個特性的評估:

  • 職責(zé)拆分 — 在例子中并不是很清晰殉了,但是事實上,MVVM的View要比MVP中的View承擔(dān)的責(zé)任多拟枚。因為前者通過ViewModel的設(shè)置綁定來更新狀態(tài)薪铜,而后者只監(jiān)聽Presenter的事件但并不會對自己有什么更新。
  • 可測性 — ViewModel不知道關(guān)于View的任何事情恩溅,這允許我們可以輕易的測試ViewModel隔箍。同時View也可以被測試,但是由于屬于UIKit的范疇脚乡,對他們的測試通常會被忽略蜒滩。
  • 易用性 — 在我們例子中的代碼量和MVP的差不多,但是在實際開發(fā)中奶稠,我們必須把View中的事件指向Presenter并且手動的來更新View俯艰,如果使用綁定的話,MVVM代碼量將會小的多锌订。

MVVM是非常有吸引力的竹握,因為它集合了上述方法的優(yōu)點,并且由于在View層的綁定辆飘,它并不需要其他附加的代碼來更新View啦辐,盡管這樣,可測試性依然很強蜈项。

VIPER

把LEGO架構(gòu)經(jīng)驗遷移到iOS app的設(shè)計

VIPER是我們最后要介紹的芹关,由于不是來自于MV(X)系列,它具備一定的趣味性战得。

到目前為止充边,你必須同意劃分責(zé)任的粒度是很好的選擇。VIPER在責(zé)任劃分層面進行了迭代常侦,VIPER分為五個層次:

VIPER
  • 交互器(Interactor) — 包括關(guān)于數(shù)據(jù)和網(wǎng)絡(luò)請求的業(yè)務(wù)邏輯浇冰,例如創(chuàng)建一個實體(Entities),或者從服務(wù)器中獲取一些數(shù)據(jù)聋亡。為了實現(xiàn)這些功能肘习,需要使用服務(wù)、管理器坡倔,但是他們并不被認為是VIPER架構(gòu)內(nèi)的模塊漂佩,而是外部依賴脖含。
  • 展示器(Presenter) — 包含UI層面(但UIKit獨立)的業(yè)務(wù)邏輯以及在交互器(Interactor)層面的方法調(diào)用。
  • 實體(Entities) — 普通的數(shù)據(jù)對象投蝉,不屬于數(shù)據(jù)訪問層养葵,因為數(shù)據(jù)訪問屬于交互器(Interactor)的職責(zé)。
  • 路由器(Router) — 用來連接VIPER的各個模塊瘩缆。

基本上关拒,VIPER的模塊可以是一個屏幕或者用戶使用應(yīng)用的整個過程 —— 例如認證過程,可以由一屏完成或者需要幾步才能完成庸娱。你想讓模塊多大着绊,這取決于你。

當(dāng)我們把VIPER和MV(X)系列作比較時熟尉,我們會在職責(zé)劃分方面發(fā)現(xiàn)一些不同:

  • Model(數(shù)據(jù)交互)邏輯以實體(Entities)為單位拆分到交互器(Interactor)中归露。
  • Controller/Presenter/ViewModel 的UI展示方面的職責(zé)移到了Presenter中,但是并沒有數(shù)據(jù)轉(zhuǎn)換相關(guān)的操作斤儿。
  • VIPER 是第一個通過路由器(Router)實現(xiàn)明確的地址導(dǎo)航的模式剧包。

找到一個適合的方法來實現(xiàn)路由對于iOS應(yīng)用是一個挑戰(zhàn),MV(X)系列并未涉及這一問題雇毫。

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

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
    }
    
    // 布局代碼
}
// 裝配 VIPER 模塊(不包含路由)
let view = GreetingViewController()
let presenter = GreetingPresenter()
let interactor = GreetingInteractor()
view.eventHandler = presenter
presenter.view = view
presenter.greetingProvider = interactor
interactor.output = presenter

讓我們再來評估一下特性:

  • 職責(zé)拆分 — 毫無疑問棚放,VIPER是任務(wù)劃分中的佼佼者。
  • 可測性 — 不出意外地馅闽,更好的分布性就有更好的可測試性飘蚯。
  • 易用性 — 最后你可能已經(jīng)猜到了維護成本方面的問題。你必須為很小功能的類寫出大量的接口福也。
什么是LEGO

當(dāng)使用VIPER時局骤,你可能想像用樂高積木來搭建一個城堡,這個想法可能存在一些問題暴凑。也許峦甩,現(xiàn)在就應(yīng)用VIPER架構(gòu)還為時過早,考慮一些更為簡單的模式反而會更好现喳。一些人會忽略這些問題凯傲,大材小用。假定他們篤信VIPER架構(gòu)會在未來給他們的應(yīng)用帶來一些好處嗦篱,雖然現(xiàn)在維護起來確實是有些費勁冰单。如果你也持這樣的觀點,我為你推薦 Generamba 這個用來搭建VIPER架構(gòu)的工具灸促。雖然我個人感覺這是在用高射炮打蚊子诫欠。

總結(jié)

我們研究了幾種架構(gòu)模式涵卵,希望你能找到一些困擾你的問題的答案。但毫無疑問通過閱讀這篇文章你應(yīng)該已經(jīng)認識到了沒有絕對的解決方案荒叼。所以架構(gòu)模式的選擇需要根據(jù)實際情況進行利弊分析轿偎。
因此,在同一應(yīng)用程序中混合架構(gòu)是很自然的被廓。例如:你開始的時候使用MVC贴硫,然后突然意識到一個頁面在MVC模式下的變得越來越難以維護,然后就切換到MVVM架構(gòu)伊者,但是僅僅針對這一個頁面英遭。并沒有必要對哪些MVC模式下運轉(zhuǎn)良好的頁面進行重構(gòu),因為二者是可以并存的亦渗。

讓一切盡可能簡單挖诸,但不是愚蠢。 ?—— ?阿爾伯特·愛因斯坦

譯自:iOS Architecture Patterns

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末法精,一起剝皮案震驚了整個濱河市多律,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搂蜓,老刑警劉巖狼荞,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異帮碰,居然都是意外死亡相味,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門殉挽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丰涉,“玉大人,你說我怎么就攤上這事斯碌∫凰溃” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵傻唾,是天一觀的道長投慈。 經(jīng)常有香客問我,道長冠骄,這世上最難降的妖魔是什么伪煤? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮猴抹,結(jié)果婚禮上带族,老公的妹妹穿的比我還像新娘。我一直安慰自己蟀给,他們只是感情好蝙砌,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布璃诀。 她就那樣靜靜地躺著愉阎,像睡著了一般比默。 火紅的嫁衣襯著肌膚如雪鸣峭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天肚邢,我揣著相機與錄音壹堰,去河邊找鬼。 笑死骡湖,一個胖子當(dāng)著我的面吹牛贱纠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播响蕴,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼谆焊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了浦夷?” 一聲冷哼從身側(cè)響起辖试,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎劈狐,沒想到半個月后罐孝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡肥缔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年莲兢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辫继。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡怒见,死狀恐怖俗慈,靈堂內(nèi)的尸體忽然破棺而出姑宽,到底是詐尸還是另有隱情,我是刑警寧澤闺阱,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布炮车,位于F島的核電站,受9級特大地震影響酣溃,放射性物質(zhì)發(fā)生泄漏瘦穆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一赊豌、第九天 我趴在偏房一處隱蔽的房頂上張望扛或。 院中可真熱鬧,春花似錦碘饼、人聲如沸熙兔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽住涉。三九已至麸锉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舆声,已是汗流浹背花沉。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留媳握,地道東北人碱屁。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像蛾找,于是被迫代替她去往敵國和親忽媒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容