自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

iOS架構(gòu)師的必經(jīng)之路

移動開發(fā)
因為假如你不關(guān)心架構(gòu),那么總有一天,需要在同一個龐大的類中調(diào)試若干復(fù)雜的事情,你會發(fā)現(xiàn)在這樣的條件下,根本不可能在這個類中快速的找到以及有效的修改任何bug.當(dāng)然,把這樣的一個類想象為一個整體是困難的,因此,有可能一些重要的細(xì)節(jié)總會在這個過程中會被忽略。

序言

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

因為假如你不關(guān)心架構(gòu),那么總有一天,需要在同一個龐大的類中調(diào)試若干復(fù)雜的事情,你會發(fā)現(xiàn)在這樣的條件下,根本不可能在這個類中快速的找到以及有效的修改任何bug.當(dāng)然,把這樣的一個類想象為一個整體是困難的,因此,有可能一些重要的細(xì)節(jié)總會在這個過程中會被忽略。

架構(gòu)模式

一個整體的分層? 邏輯清晰? 還是清晰的分工? 對于架構(gòu)模式來說并沒有一個非常明確的定義, 比較抽象, 在于設(shè)計在于架構(gòu), 不管是小到類與類之間的交互, 還是不同的小模塊, 小版塊之間, 甚至于在不同的業(yè)務(wù)之間, 我們都可以去從架構(gòu)的方面去理解, 去分析。

那么在iOS中常見的架構(gòu)來說, 常見有就有這些: MVC架構(gòu), MVP架構(gòu), MVVM架構(gòu)

當(dāng)然這些架構(gòu)都有一個共同特點: 解耦合

MVC

什么是MVC?

MVC最早存在于桌面程序中的, M是指業(yè)務(wù)數(shù)據(jù), V是指用戶界面, C則是控制器. 在具體的業(yè)務(wù)場景中, C作為M和V之間的連接, 負(fù)責(zé)獲取輸入的業(yè)務(wù)數(shù)據(jù), 然后將處理后的數(shù)據(jù)輸出到界面上做相應(yīng)展示, 另外, 在數(shù)據(jù)有所更新時, C還需要及時提交相應(yīng)更新到界面展示. 在上述過程中, 因為M和V之間是完全隔離的, 所以在業(yè)務(wù)場景切換時, 通常只需要替換相應(yīng)的C, 復(fù)用已有的M和V便可快速搭建新的業(yè)務(wù)場景. MVC因其復(fù)用性, 大大提高了開發(fā)效率, 現(xiàn)已被廣泛應(yīng)用在各端開發(fā)中。

MVC的過去

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

 

8654141-b55234aa6c7e6989.png

在圖里,View并沒有任何界限,僅僅是簡單的在Controller中呈現(xiàn)出Model的變化。想象一下,就像網(wǎng)頁一樣,在點擊了跳轉(zhuǎn)到某個其他頁面的連接之后就會完全的重新加載頁面。盡管在iOS平臺上實現(xiàn)這這種MVC模式是沒有任何難度的,但是它并不會為我們解決架構(gòu)問題帶來任何裨益。因為它本身也是,三個實體間相互都有通信,而且是緊密耦合的。這很顯然會大大降低了三者的復(fù)用性,而這正是我們不愿意看到的。

然而傳統(tǒng)的MVC架構(gòu)不適用于現(xiàn)在的iOS開發(fā)

蘋果推薦的MVC---愿景

 

8654141-53ffb8f211670be0.png

由于Controller是一個介于View 和 Model之間的協(xié)調(diào)器,所以View和Model之間沒有任何直接的聯(lián)系。Controller是一個最小可重用單元,這對我們來說是一個好消息,因為我們總要找一個地方來寫邏輯復(fù)雜度較高的代碼,而這些代碼又不適合放在Model中。理論上來講,這種模式看起來非常直觀,但你有沒有感到哪里有一絲詭異?你甚至聽說過,有人將MVC的縮寫展開成(Massive View Controller),更有甚者,為View Controller減負(fù)也成為iOS開發(fā)者面臨的一個重要話題。如果蘋果繼承并且對MVC模式有一些進展,所有這些為什么還會發(fā)生?

蘋果推薦的MVC--事實

 

8654141-ec804c448fbbb9d0.png

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

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

 

  1. varuserCell = tableView.dequeueReusableCellWithIdentifier("identifier"as UserCell 
  2. userCell.configureWithUser(user

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

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

我們看下一些簡單的例子:

 

  1. import UIKit 
  2. struct Person { // Model 
  3.     let firstName: String 
  4.     let lastName: String 
  5. class GreetingViewController : UIViewController { // View + Controller 
  6.     varperson: Person! 
  7.     let showGreetingButton = UIButton() 
  8.     let greetingLabel = UILabel() 
  9.     override func viewDidLoad() { 
  10.         super.viewDidLoad() 
  11.         self.showGreetingButton.addTarget(self, action"didTapButton:", forControlEvents: .TouchUpInside) 
  12.     } 
  13.     func didTapButton(button: UIButton) { 
  14.         let greeting = "Hello"" "+ self.person.firstName + " "+ self.person.lastName 
  15.         self.greetingLabel.text = greeting 
  16.     } 
  17.     // layout code goes here 
  18. // Assembling of MVC 
  19. let model = Person(firstName: "David", lastName: "Blaine"
  20. let view = GreetingViewController() 
  21. view.person = model; 

這段代碼看起來可測試性并不強,我們可以把和greeting相關(guān)的都放到GreetingModel中然后分開測試,但是這樣我們就無法通過直接調(diào)用在GreetingViewController中的UIView的方法(viewDidLoad和didTapButton方法)來測試頁面的展示邏輯了,因為一旦調(diào)用則會使整個頁面都變化,這對單元測試來講并不是什么好消息。事實上,在單獨一個模擬器中(比如iPhone 4S)加載并測試UIView并不能保證在其他設(shè)備中也能正常工作,因此我建議在單元測試的Target的設(shè)置下移除"Host Application"項,并且不要在模擬器中測試你的應(yīng)用。

以上所述,似乎Cocoa MVC 看起來是一個相當(dāng)差的架構(gòu)方案。我們來評估一下MVC一系列的特征:

  1. 任務(wù)均攤--View和Model確實是分開的,但是View和Controller卻是緊密耦合的
  2. 可測試性--由于糟糕的分散性,只能對Model進行測試
  3. 易用性--與其他幾種模式相比最小的代碼量。熟悉的人很多,因而即使對于經(jīng)驗不那么豐富的開發(fā)者來講維護起來也較為容易。

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

MVP

MVC的缺點在于并沒有區(qū)分業(yè)務(wù)邏輯和業(yè)務(wù)展示, 這對單元測試很不友好. MVP針對以上缺點做了優(yōu)化, 它將業(yè)務(wù)邏輯和業(yè)務(wù)展示也做了一層隔離, 對應(yīng)的就變成了MVCP. M和V功能不變, 原來的C現(xiàn)在只負(fù)責(zé)布局, 而所有的邏輯全都轉(zhuǎn)移到了P層。

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

 

8654141-8f9c15b8f58e5d50.png

這看起來不正是蘋果所提出的MVC方案嗎?確實是的,這種模式的名字叫做MVC,但是,這就是說蘋果的MVC實際上就是MVP了?不,并不是這樣的。如果你仔細(xì)回憶一下,View是和Controller緊密耦合的,但是MVP的協(xié)調(diào)器Presenter并沒有對ViewController的生命周期做任何改變,因此View可以很容易的被模擬出來。在Presenter中根本沒有和布局有關(guān)的代碼,但是它卻負(fù)責(zé)更新View的數(shù)據(jù)和狀態(tài)。就MVP而言,UIViewController的子類實際上就是Views并不是Presenters。這點區(qū)別使得這種模式的可測試性得到了極大的提高,付出的代價是開發(fā)速度的一些降低,因為必須要做一些手動的數(shù)據(jù)和事件綁定,從下例中可以看出:

 

  1. import UIKit 
  2. struct Person { // Model 
  3.     let firstName: String 
  4.     let lastName: String 
  5. protocol GreetingView: class { 
  6.     func setGreeting(greeting: String) 
  7. protocol GreetingViewPresenter { 
  8.     init(view: GreetingView, person: Person) 
  9.     func showGreeting() 
  10. class GreetingPresenter : GreetingViewPresenter { 
  11.     unowned let view: GreetingView 
  12.     let person: Person 
  13.     required init(view: GreetingView, person: Person) { 
  14.         self.view = view 
  15.         self.person = person 
  16.     } 
  17.     func showGreeting() { 
  18.         let greeting = "Hello"" "+ self.person.firstName + " "+ self.person.lastName 
  19.         self.view.setGreeting(greeting) 
  20.     } 
  21. class GreetingViewController : UIViewController, GreetingView { 
  22.     varpresenter: GreetingViewPresenter! 
  23.     let showGreetingButton = UIButton() 
  24.     let greetingLabel = UILabel() 
  25.     override func viewDidLoad() { 
  26.         super.viewDidLoad() 
  27.         self.showGreetingButton.addTarget(self, action"didTapButton:", forControlEvents: .TouchUpInside) 
  28.     } 
  29.     func didTapButton(button: UIButton) { 
  30.         self.presenter.showGreeting() 
  31.     } 
  32.     func setGreeting(greeting: String) { 
  33.         self.greetingLabel.text = greeting 
  34.     } 
  35.     // layout code goes here 
  36. // Assembling of MVP 
  37. let model = Person(firstName: "David", lastName: "Blaine"
  38. let view = GreetingViewController() 
  39. let presenter = GreetingPresenter(viewview, person: model) 
  40. view.presenter = presenter 

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

我們來評估一下MVP一系列的特征:

  1. 任務(wù)均攤--我們將最主要的任務(wù)劃分到Presenter和Model,而View的功能較少(雖然上述例子中Model的任務(wù)也并不多)。
  2. 可測試性--非常好,由于一個功能簡單的View層,所以測試大多數(shù)業(yè)務(wù)邏輯也變得簡單
  3. 易用性--在我們上邊不切實際的簡單的例子中,代碼量是MVC模式的2倍,但同時MVP的概念卻非常清晰

MVP--綁定和信號

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

這個變體包含了View和Model之間的直接綁定,但是Presenter仍然來管理來自View的動作事件,同時也能勝任對View的更新。

 

8654141-c7d1f62dc7d9bec1.png

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

MVVM

MVVM架構(gòu)是MV(X)系列最新的一員,因此讓我們希望它已經(jīng)考慮到MV(X)系列中之前已經(jīng)出現(xiàn)的問題。從理論層面來講MVVM看起來不錯,我們已經(jīng)非常熟悉View和Model,以及Meditor,在MVVM中它是View Model。

 

8654141-92e48ea5635d0d1b.png

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

  1. MVVM將ViewController視作View
  2. 在View和Model之間沒有緊密的聯(lián)系

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

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

綁定

我在MVP部分已經(jīng)提到這點了,但是該部分我們?nèi)詴^續(xù)討論。

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

  1. 基于KVO的綁定庫如 RZDataBinding 和 SwiftBond
  2. 完全的函數(shù)響應(yīng)式編程,比如像ReactiveCocoa、RxSwift或者 PromiseKit

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

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

 

8654141-30a9a677f6802e0d.png

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

 

  1. import UIKit 
  2. struct Person { // Model 
  3.     let firstName: String 
  4.     let lastName: String 
  5. protocol GreetingViewModelProtocol: class { 
  6.     vargreeting: String? { get } 
  7.     vargreetingDidChange: ((GreetingViewModelProtocol) -> ())? { get set } // function to call when greeting did change 
  8.     init(person: Person) 
  9.     func showGreeting() 
  10. class GreetingViewModel : GreetingViewModelProtocol { 
  11.     let person: Person 
  12.     vargreeting: String? { 
  13.         didSet { 
  14.             self.greetingDidChange?(self) 
  15.         } 
  16.     } 
  17.     vargreetingDidChange: ((GreetingViewModelProtocol) -> ())? 
  18.     required init(person: Person) { 
  19.         self.person = person 
  20.     } 
  21.     func showGreeting() { 
  22.         self.greeting = "Hello"" "+ self.person.firstName + " "+ self.person.lastName 
  23.     } 
  24. class GreetingViewController : UIViewController { 
  25.     varviewModel: GreetingViewModelProtocol! { 
  26.         didSet { 
  27.             self.viewModel.greetingDidChange = { [unowned self] viewModel in 
  28.                 self.greetingLabel.text = viewModel.greeting 
  29.             } 
  30.         } 
  31.     } 
  32.     let showGreetingButton = UIButton() 
  33.     let greetingLabel = UILabel() 
  34.     override func viewDidLoad() { 
  35.         super.viewDidLoad() 
  36.         self.showGreetingButton.addTarget(self.viewModel, action"showGreeting", forControlEvents: .TouchUpInside) 
  37.     } 
  38.     // layout code goes here 
  39. // Assembling of MVVM 
  40. let model = Person(firstName: "David", lastName: "Blaine"
  41. let viewModel = GreetingViewModel(person: model) 
  42. let view = GreetingViewController() 
  43. view.viewModel = viewModel 

我們再來評估一下MVVM一系列的特征:

  • 任務(wù)均攤 -- 在例子中并不是很清晰,但是事實上,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代碼量將會小的多。

總結(jié)

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

因此,在同一個應(yīng)用中包含著多種架構(gòu)。比如,你開始的時候使用MVC,然后突然意識到一個頁面在MVC模式下的變得越來越難以維護,然后就切換到MVVM架構(gòu),但是僅僅針對這一個頁面。并沒有必要對哪些MVC模式下運轉(zhuǎn)良好的頁面進行重構(gòu),因為二者是可以并存的。

責(zé)任編輯:未麗燕 來源: iOS小迷糊
相關(guān)推薦

2017-03-21 13:00:05

網(wǎng)絡(luò)融合光網(wǎng)絡(luò)通信網(wǎng)絡(luò)

2010-11-03 10:12:58

信息安全服務(wù)必經(jīng)之路

2017-09-18 17:00:04

云深度學(xué)習(xí)機器學(xué)習(xí)云服務(wù)

2015-10-13 13:54:16

2019-08-12 07:45:44

Linux腳本shell

2019-07-29 08:07:57

工業(yè)互聯(lián)網(wǎng)互聯(lián)網(wǎng)平臺IIOT

2018-08-22 16:35:31

存儲

2024-04-26 00:10:23

Python代碼開發(fā)

2019-07-23 18:15:26

技術(shù)大數(shù)據(jù)數(shù)據(jù)庫

2021-10-25 09:41:04

架構(gòu)運維技術(shù)

2019-03-19 20:09:35

5G毫米波移動通信

2018-07-03 15:46:24

Java架構(gòu)師源碼

2020-11-25 09:56:48

架構(gòu)運維技術(shù)

2022-06-15 10:04:51

存儲選型MySQL

2012-09-10 09:55:25

2013-04-15 10:09:18

Web架構(gòu)師

2013-04-15 11:08:00

架構(gòu)師Web

2009-02-23 11:18:06

J2EE架構(gòu)師Java
點贊
收藏

51CTO技術(shù)棧公眾號