MVVM的特別注意(轉(zhuǎn))

在學(xué)習(xí)MVVM的時候首先提出幾個問題:

MVVM到底是什么?它和MVC有什么區(qū)別?

MVVM中VM到底是個什么角色?它和Controller或者M(jìn)anager有什么區(qū)別?

ViewController在MVVM中扮演怎樣角色?Api數(shù)據(jù)請求放在哪里?數(shù)據(jù)流向如何?

MVVM簡介

關(guān)于MVVM,相信大家或多或少都有了解。引用MVVM介紹文中一圖:

受MVC或MVP架構(gòu)的影響,對MVVM最初印象以為這是一個以ViewModel為核心,處理View和Model的開發(fā)架構(gòu)。于是乎在原有MVC的基礎(chǔ)上,創(chuàng)建了一個所謂的ViewModel對象,然后把ViewController中的代碼移到ViewModel中,在ViewModel里面處理View以及Model的所有邏輯。畢竟大家都在說MVVM可以為ViewController瘦身,這ViewController就剩創(chuàng)建ViewModel的代碼,嗯,夠瘦身,這就是MVVM!

慢慢的,發(fā)現(xiàn)有什么地方不對,哪里不對?第一想法就是ViewController的定位,View?不是,Controller?也不是!畢竟它就創(chuàng)建ViewModel,好像與View、Model也沒啥關(guān)系。拋開ViewController不談,突然發(fā)現(xiàn)這樣的ViewModel、Model以及View不就是MVC,一個以ViewModel為中心的MVC!

錯在哪里

核心問題就在于對ViewModel角色的定位不清!基于MVVM設(shè)計(jì)思路,ViewModel存在目的在于抽離ViewController中展示業(yè)務(wù)邏輯,而不是替代ViewController,其它視圖操作業(yè)務(wù)等還是應(yīng)該放在ViewController中實(shí)現(xiàn)。

既然不負(fù)責(zé)視圖操作邏輯,ViewModel中就不應(yīng)該存在任何View對象,更不應(yīng)該存在Push/Present等視圖跳轉(zhuǎn)邏輯。因此,ViewModel中絕不應(yīng)該存在任何視圖操作相關(guān)的代碼

@interface?ViewModel?:?NSObject

//?viewmodel中切不可存在view對象,更不該出現(xiàn)push或者present代碼

-?(instancetype)initWithTableView:(UITableView?*)tableView;

@end

很簡單,處理視圖展示邏輯,ViewModel負(fù)責(zé)將數(shù)據(jù)業(yè)務(wù)層提供的數(shù)據(jù)轉(zhuǎn)化為界面展示所需的VO。其與View一一對應(yīng),沒有View就沒有ViewModel。

比如,數(shù)據(jù)業(yè)務(wù)層傳遞一個含有性別屬性sex的DO對象,0表示男, 1表示女。ViewModel的職責(zé)就是將其轉(zhuǎn)化為展示層可顯示的VO對象

self.personVO.sex = personDO.sex == 0 ? @"男": @"女";

ViewModel和View一起組成DDD(Model-Driven Design)領(lǐng)域驅(qū)動架構(gòu)體系中的Presentation展示層。在iOS中,數(shù)據(jù)流向可以表示為ViewModel->ViewController->View,ViewController負(fù)責(zé)連接VO及其對應(yīng)的View對象

領(lǐng)域驅(qū)動設(shè)計(jì)

領(lǐng)域驅(qū)動設(shè)計(jì)(DDD)對于安卓童鞋可能非常熟悉,有興趣的童鞋可以參考這篇文章,本文不做過多講解,借用其描述介紹幾個名詞

VO(View Object):視圖對象,用于展示層,它的作用是把某個指定頁面(或組件)的所有數(shù)據(jù)封裝起來

DO(Domain Object):領(lǐng)域?qū)ο?,就是從現(xiàn)實(shí)世界中抽象出來的有形或無形的業(yè)務(wù)實(shí)體

PO(Persistent Object):持久化對象,它跟持久層(通常是關(guān)系型數(shù)據(jù)庫)的數(shù)據(jù)結(jié)構(gòu)形成一一對應(yīng)的映射關(guān)系,如果持久層是關(guān)系型數(shù)據(jù)庫,那么,數(shù)據(jù)表中的每個字段(或若干個)就對應(yīng)PO的一個(或若干個)屬性

Domain:領(lǐng)域驅(qū)動層,是用戶與數(shù)據(jù)庫交互的核心中轉(zhuǎn)站,控制用戶數(shù)據(jù)收集,控制請求轉(zhuǎn)向等

MVVM架構(gòu)中,ViewModel連接視圖View和數(shù)據(jù)業(yè)務(wù)Model層,而Domain和Data數(shù)據(jù)持久層共同組成整個Model層。完整結(jié)構(gòu)如圖所示:


Model并不表示Model

MVVM架構(gòu)中的M,并不表示Model對象,而是表示整個數(shù)據(jù)業(yè)務(wù)層,對應(yīng)DDD架構(gòu)中的Domain層以及數(shù)據(jù)Data層。業(yè)務(wù)開發(fā)中,一般考慮Api或者DB對象,極少考慮Domain層設(shè)計(jì),也不會區(qū)分DO或者PO對象?;\統(tǒng)定義Model ,將其傳遞給展示層ViewModel,久而久之,Model對象承載的信息越來越多,更有甚者,在Model中處理業(yè)務(wù)邏輯,導(dǎo)致項(xiàng)目維護(hù)成本增加,代碼中出現(xiàn)if..else的概率也會越來越大:

@interface?PersonModel?:?NSObject

@property?(nonatomic,?assign)?NSInteger?sex;

@property?(nonatomic,?readonly)?NSString?*sexDescription;

@end

@implementation?PersonModel

//?model中不應(yīng)該存在業(yè)務(wù)邏輯代碼

-?(NSString?*)sexDescription?{

returnself.sex?==?0???@"男":?@"女";

}

@end

當(dāng)然,Domain層并不是必須的,實(shí)際開發(fā)中,需要根據(jù)具體復(fù)雜度和需求來決定。比如只是純粹的請求展示界面,設(shè)計(jì)過多的層次結(jié)構(gòu)反而會增加項(xiàng)目的維護(hù)成本。同時,Domain層不應(yīng)該存在任何狀態(tài)變量!

Data數(shù)據(jù)層

ViewModel負(fù)責(zé)展示層邏輯,而Data層則對應(yīng)數(shù)據(jù)層邏輯,一般以Manager或者Service身份存在,數(shù)據(jù)來源主要包括Api、DB或者Cache等。Data數(shù)據(jù)層操作對象主要為PO持久化對象,對象一旦創(chuàng)建,原則上不可修改

Data數(shù)據(jù)層具有獨(dú)立可測試性,其不依賴視圖層而存在。切記不可在Data層操作任何視圖對象!

@implementation?PersonDBAccess

//?Data層不應(yīng)該存在任何視圖相關(guān)代碼

-?(NSArray?*)fetchPersonModels?{

[SVProgressHUD?showWithStatus:@"加載中。。。"];

}

@end

MVVM奇葩說

文章到此,想必各位對MVVM架構(gòu)已經(jīng)有了大致了解。對比安卓童鞋對MVP架構(gòu)的鐘愛,iOS童鞋也許更加青睞MVVM,拌上ReactiveCocoa或者RxSwift,這道菜可以做的更加絢爛多彩!當(dāng)然,正如唐巧在文中所言:ReactiveCocoa 和 MVVM 不應(yīng)該被神化,我們需要保持的是一個擁抱變化的心,以及理性分析的態(tài)度。在新技術(shù)的面前,不盲從,也不守舊,一切的決策都應(yīng)該建立在認(rèn)真分析的基礎(chǔ)上,這樣才能應(yīng)對技術(shù)的變化!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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