iOS 關(guān)于MVC和MVVM設(shè)計(jì)模式的那些事

一、概述
  • 在 iOS 開(kāi)發(fā)中,MVC(Model View Controller)是構(gòu)建iOS App的標(biāo)準(zhǔn)模式,是蘋(píng)果推薦的一個(gè)用來(lái)組織代碼的權(quán)威范式。Apple甚至是這么說(shuō)的。在MVC下,所有的對(duì)象被歸類為一個(gè)Model,一個(gè)View,和一個(gè)Controller。Model持有數(shù)據(jù),View顯示與用戶交互的界面,而ViewController調(diào)解ModelView之間的交互?,F(xiàn)在,MVC 依然是目前主流客戶端編程框架,但同時(shí)它也被調(diào)侃成Massive View Controller(重量級(jí)視圖控制器),想必開(kāi)發(fā)者在開(kāi)發(fā)中無(wú)可避免被下面幾個(gè)問(wèn)題所困擾:

    • 厚重的ViewController
    • 遺失的網(wǎng)絡(luò)邏輯(無(wú)立足之地)
    • 較差的可測(cè)試性
  • 為了避免和解決上述問(wèn)題的產(chǎn)生,從MVC引申出來(lái)一種維護(hù)性較強(qiáng)、耦合性低的新的架構(gòu)MVVM(Model View View-Mode),MVVM正式規(guī)范了視圖和控制器緊耦合的性質(zhì),并引入新的組件。MVVM主要目的是為了分離視圖(View)模型(Model)。

  • 本文只是分享一下筆者對(duì)MVCMVVM的一些見(jiàn)解,在此拋磚引玉,希望能為存在對(duì)MVCMVVM迷茫的廣大開(kāi)發(fā)者提供一點(diǎn)思路,少走一些彎路,填補(bǔ)一些細(xì)坑。文章僅供大家參考,若有不妥之處,還望不吝賜教,歡迎批評(píng)指正。

二、MVC(Model View Controller)
  1. MVC之間的關(guān)系
    任何一個(gè)正經(jīng)開(kāi)發(fā)過(guò)軟件的人都熟悉MVC,它意思是Model View Controller, 是一個(gè)在復(fù)雜應(yīng)用設(shè)計(jì)中組織代碼的公認(rèn)模式,它們之間的結(jié)構(gòu)關(guān)系如下:
    MVC示意圖.png

我們看到的只是一個(gè)蘋(píng)果 **典型的MVC ** 設(shè)置。view將用戶交互通知給controller。view controller通過(guò)更新model來(lái)反應(yīng)狀態(tài)的改變。model(通常使用Key-Value-Observation)通知controller來(lái)更新他們負(fù)責(zé)的view。大多數(shù)iOS應(yīng)用程序的代碼使用這種方式來(lái)組織。然而,典型的MVC架構(gòu)不適用于當(dāng)下的iOS開(kāi)發(fā)。盡管從技術(shù)上看ViewController 是相互獨(dú)立的,但事實(shí)上它們幾乎總是結(jié)對(duì)出現(xiàn),一個(gè) View 只能與一個(gè) Controller 進(jìn)行匹配,反之亦然。既然如此,那我們?yōu)楹尾徽?guī)化它們的連接:

MVC示意圖2.png

因此,M-VC 可能是對(duì) iOS 開(kāi)發(fā)中的 MVC模式更為準(zhǔn)確的解讀,同時(shí)更也準(zhǔn)確地描述了我們?nèi)粘i_(kāi)發(fā)可能已經(jīng)編寫(xiě)的 MVC 代碼,但它并沒(méi)有做太多事情來(lái)解決 iOS 應(yīng)用中日益增長(zhǎng)的重量級(jí)視圖控制器的問(wèn)題。(PS:躺槍了沒(méi)...)

舉例說(shuō)明:


M-VC_Example.png

若假設(shè)筆者利用MVC的設(shè)計(jì)模式來(lái)開(kāi)發(fā)此界面,那想必是這樣的。

  • M:SUGoods(商品模型Model)
  • V:SUGoodsCell(展示商品數(shù)據(jù)的View,自定義的 UITableViewCell)
  • C:SUHomeViewController (首頁(yè)控制器Controller)

控制器(SUHomeViewController)代碼實(shí)現(xiàn)

- (void) requestRemoteData
{
   // 1.發(fā)起網(wǎng)絡(luò)請(qǐng)求,獲取到服務(wù)器的數(shù)據(jù),并將其轉(zhuǎn)化成模型數(shù)據(jù)(`SUGoods`)
   // 2.添加到數(shù)據(jù)源(`dataSource`)
   // 3.最后刷新表格`[self.tableView reloadData]`,配置`SUGoodsCel`l即可
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    SUGoodsCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Goods"];
    cell.goods = self.dataSource[indexPath.row];
    return cell;
}

View(SUGoodsCell)代碼實(shí)現(xiàn)

SUGoodsCell.h
@class SUGoods;
@interface SUGoodsCell : UITableViewCell
@property (nonatomic, strong) SUGoods *goods;
@end

SUGoodsCell.m
@implementation SUGoodsCell
- (void)setGoods:(SUGoods *)goods
{
     _goods = goods
     /// config data ....
}
@end 

都寫(xiě)到這份上了,大家用腳趾頭想想,這個(gè)SUGoodsCell,正是由View直接來(lái)調(diào)用Model,所以事實(shí)上典型的MVC的原則已經(jīng)違背了,但是這種情況是一直發(fā)生的甚至于人們不覺(jué)得這里有哪些不對(duì)。如果嚴(yán)格遵守MVC的話,你會(huì)把對(duì)cell的設(shè)置放在Controller中,不向View傳遞一個(gè)Model對(duì)象,這樣就會(huì)大大增加Controller的體積。所以說(shuō),這哪里是典型的MVC設(shè)計(jì)模式,這分明是M-VC設(shè)計(jì)模式呀。簡(jiǎn)而言之,在理想的世界里,MVC也許工作的很好。然而,我們生活在真實(shí)的世界,謝謝(PS:讓夢(mèng)想實(shí)現(xiàn)的最好的方式,就是醒來(lái)?。。。?。

  1. MVC的弊端
    MVC的利弊大家想必是有目共睹的,Massive View Controller的說(shuō)法也并非空穴來(lái)風(fēng)的。讓我們一起探討MVC的弊端,剖析問(wèn)題產(chǎn)生原因,打造一個(gè)輕量級(jí)的ViewController,明確MVC設(shè)計(jì)模式中各個(gè)角色的職責(zé)。
  • 厚重的View Controller
    M:模型model的對(duì)象通常非常的簡(jiǎn)單。根據(jù)Apple的文檔,model應(yīng)包括數(shù)據(jù)操作數(shù)據(jù)的業(yè)務(wù)邏輯。而在實(shí)踐中,model層往往非常薄,不管怎樣,model層的業(yè)務(wù)邏輯不應(yīng)被拖入到controller。
    V:視圖view通常是UIKit控件(component,這里根據(jù)習(xí)慣譯為控件)或者編碼定義的UIKit控件的集合。View的如何構(gòu)建(PS:IB或者手寫(xiě)界面)何必讓Controller知曉,同時(shí)View不應(yīng)該直接引用model(PS:現(xiàn)實(shí)中,你懂的?。?,并且僅僅通過(guò)IBAction事件引用controller。業(yè)務(wù)邏輯很明顯不歸入view,視圖本身沒(méi)有任何業(yè)務(wù)。
    C:控制器controllerController是app的“膠水代碼”:協(xié)調(diào)模型和視圖之間的所有交互??刂破髫?fù)責(zé)管理他們所擁有的視圖的視圖層次結(jié)構(gòu),還要響應(yīng)視圖的loading、appearing、disappearing等等,同時(shí)往往也會(huì)充滿我們不愿暴露的model的模型邏輯以及不愿暴露給視圖的業(yè)務(wù)邏輯。
    網(wǎng)絡(luò)數(shù)據(jù)的請(qǐng)求及后續(xù)處理,本地?cái)?shù)據(jù)庫(kù)操作,以及一些帶有工具性質(zhì)輔助方法都加大了Massive View Controller的產(chǎn)生。

  • 遺失(無(wú)處安放)的網(wǎng)絡(luò)邏輯
    蘋(píng)果使用的MVC的定義是這么說(shuō)的:所有的對(duì)象都可以被歸類為一個(gè)model,一個(gè)view,或是一個(gè)controller。
    你可能試著把它放在Model對(duì)象里,但是也會(huì)很棘手,因?yàn)榫W(wǎng)絡(luò)調(diào)用應(yīng)該使用異步,這樣如果一個(gè)網(wǎng)絡(luò)請(qǐng)求比持有它的model生命周期更長(zhǎng),事情將變的復(fù)雜。顯然View里面做網(wǎng)絡(luò)請(qǐng)求那就更格格不入了,因此只剩下Controller了。若這樣,這又加劇了Massive View Controller的問(wèn)題。若不這樣,何處才是網(wǎng)絡(luò)邏輯的家呢?

  • 較差的可測(cè)試性
    由于View Controller混合了視圖處理邏輯和業(yè)務(wù)邏輯,分離這些成分的單元測(cè)試成了一個(gè)艱巨的任務(wù)。若一個(gè)Massive View Controller有上萬(wàn)行代碼,要你編寫(xiě)個(gè)單元測(cè)試,我敢保證,你不是想寫(xiě),你是想死,分分鐘填表走人。

三、MVVM(Model View View-Mode)

一種可以很好地解決Massive View Controller問(wèn)題的辦法就是將 Controller 中的展示邏輯抽取出來(lái),放置到一個(gè)專門(mén)的地方,而這個(gè)地方就是 viewModel 。MVVM衍生于MVC,是對(duì) MVC 的一種演進(jìn),它促進(jìn)了 UI 代碼與業(yè)務(wù)邏輯的分離。它正式規(guī)范了視圖和控制器緊耦合的性質(zhì),并引入新的組件。他們之間的結(jié)構(gòu)關(guān)系如下:

MVVM示意圖.png

  • MVVM 的基本概念

    • MVVM 中,viewview controller正式聯(lián)系在一起,我們把它們視為一個(gè)組件
    • viewview controller 都不能直接引用model,而是引用視圖模型(viewModel
    • viewModel 是一個(gè)放置用戶輸入驗(yàn)證邏輯,視圖顯示邏輯,發(fā)起網(wǎng)絡(luò)請(qǐng)求和其他代碼的地方
    • 使用MVVM會(huì)輕微的增加代碼量,但總體上減少了代碼的復(fù)雜性
  • MVVM 的注意事項(xiàng)

    • view 引用viewModel ,但反過(guò)來(lái)不行(即不要在viewModel中引入#import UIKit.h,任何視圖本身的引用都不應(yīng)該放在viewModel中)(PS:基本要求,必須滿足
    • viewModel 引用model,但反過(guò)來(lái)不行
  • MVVM 的使用建議

    • MVVM 可以兼容你當(dāng)下使用的MVC架構(gòu)。
    • MVVM 增加你的應(yīng)用的可測(cè)試性。
    • MVVM 配合一個(gè)綁定機(jī)制效果最好(PS:ReactiveCocoa你值得擁有)。
    • viewController 盡量不涉及業(yè)務(wù)邏輯,讓 viewModel 去做這些事情。
    • viewController 只是一個(gè)中間人,接收 view 的事件、調(diào)用 viewModel 的方法、響應(yīng) viewModel 的變化。
    • viewModel 絕對(duì)不能包含視圖 view(UIKit.h),不然就跟 view 產(chǎn)生了耦合,不方便復(fù)用和測(cè)試。
    • viewModel之間可以有依賴。
    • viewModel避免過(guò)于臃腫,否則重蹈Controller的覆轍,變得難以維護(hù)。
  • MVVM 的優(yōu)勢(shì)

    • 低耦合:View 可以獨(dú)立于Model變化和修改,一個(gè) viewModel 可以綁定到不同的 View
    • 可重用性:可以把一些視圖邏輯放在一個(gè) viewModel里面,讓很多 view 重用這段視圖邏輯
    • 獨(dú)立開(kāi)發(fā):開(kāi)發(fā)人員可以專注于業(yè)務(wù)邏輯和數(shù)據(jù)的開(kāi)發(fā) viewModel,設(shè)計(jì)人員可以專注于頁(yè)面設(shè)計(jì)
    • 可測(cè)試:通常界面是比較難于測(cè)試的,而 MVVM 模式可以針對(duì) viewModel來(lái)進(jìn)行測(cè)試
  • MVVM 的弊端

    • 數(shù)據(jù)綁定使得Bug 很難被調(diào)試。你看到界面異常了,有可能是你 View 的代碼有 Bug,也可能是 Model 的代碼有問(wèn)題。數(shù)據(jù)綁定使得一個(gè)位置的 Bug 被快速傳遞到別的位置,要定位原始出問(wèn)題的地方就變得不那么容易了。
    • 對(duì)于過(guò)大的項(xiàng)目,數(shù)據(jù)綁定和數(shù)據(jù)轉(zhuǎn)化需要花費(fèi)更多的內(nèi)存(成本)。主要成本在于:
      • 數(shù)組內(nèi)容的轉(zhuǎn)化成本較高:數(shù)組里面每項(xiàng)都要轉(zhuǎn)化成Item對(duì)象,如果Item對(duì)象中還有類似數(shù)組,就很頭疼。
      • 轉(zhuǎn)化之后的數(shù)據(jù)在大部分情況是不能直接被展示的,為了能夠被展示,還需要第二次轉(zhuǎn)化。
      • 只有在API返回的數(shù)據(jù)高度標(biāo)準(zhǔn)化時(shí),這些對(duì)象原型(Item)的可復(fù)用程度才高,否則容易出現(xiàn)類型爆炸,提高維護(hù)成本。
      • 調(diào)試時(shí)通過(guò)對(duì)象原型查看數(shù)據(jù)內(nèi)容不如直接通過(guò)NSDictionary/NSArray直觀。
      • 同一API的數(shù)據(jù)被不同View展示時(shí),難以控制數(shù)據(jù)轉(zhuǎn)化的代碼,它們有可能會(huì)散落在任何需要的地方。
四、總結(jié)
  • MVC的設(shè)計(jì)模式也并非是病入膏肓,無(wú)藥可救的架構(gòu),最起碼目前MVC設(shè)計(jì)模式仍舊是iOS開(kāi)發(fā)的主流框架,存在即合理。針對(duì)文章所述的弊端,我們依舊有許多可行的方法去避免和解決,從而打造一個(gè)輕量級(jí)的ViewController
  • MVVMMVC的升級(jí)版,完全兼容當(dāng)前的MVC架構(gòu),MVVM雖然促進(jìn)了UI 代碼與業(yè)務(wù)邏輯的分離,一定程度上減輕了ViewController的臃腫度,但是ViewViewModel之間的數(shù)據(jù)綁定使得 MVVM變得復(fù)雜和難用了,如果我們不能更好的駕馭兩者之間的數(shù)據(jù)綁定,同樣會(huì)造成Controller 代碼過(guò)于復(fù)雜,代碼邏輯不易維護(hù)的問(wèn)題。
  • 一個(gè)輕量級(jí)的ViewController是基于MVCMVVM模式進(jìn)行代碼職責(zé)的分離而打造的。MVCMVVM有優(yōu)點(diǎn)也有缺點(diǎn),但缺點(diǎn)在他們所帶來(lái)的好處面前時(shí)不值一提的。他們的低耦合性,封裝性,可測(cè)試性,可維護(hù)性和多人協(xié)作便利大大提高了開(kāi)法效率。
  • 同時(shí),我們需要保持的是一個(gè)擁抱變化的心,以及理性分析的態(tài)度。在新技術(shù)的面前,不盲從,也不守舊,一切的決策都應(yīng)該建立在認(rèn)真分析的基礎(chǔ)上,這樣才能應(yīng)對(duì)技術(shù)的變化。
五、期待
  1. 文章若對(duì)您有點(diǎn)幫助,請(qǐng)給個(gè)喜歡??,畢竟碼字不易;若對(duì)您沒(méi)啥幫助,請(qǐng)給點(diǎn)建議??,切記學(xué)無(wú)止境。
  2. 針對(duì)文章所述內(nèi)容,閱讀期間任何疑問(wèn);請(qǐng)?jiān)谖恼碌撞颗u(píng)指正,我會(huì)火速解決和修正問(wèn)題。
  3. GitHub地址:https://github.com/CoderMikeHe
六、實(shí)戰(zhàn)篇
七、參考鏈接
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容