我是不敢輕易談MVVM架構(gòu)設(shè)計的
終于在經(jīng)過前面幾篇文章內(nèi)容的鋪墊之后,現(xiàn)在簡單說說自己的想法
切記,如果沒有kvc kvo的原理知識鋪墊,最好去復(fù)習(xí)一下,否則看過了解,回頭就會忘卻,不會形成意識
再次提醒:務(wù)必去了解一下 kvc kvo原理
因?yàn)槲以谖闹蠱VVM的架構(gòu)設(shè)計會依賴自定義KVO機(jī)制去實(shí)現(xiàn)
MVVM也會基于之前的 iOS架構(gòu)設(shè)計(二)- MVP 去實(shí)現(xiàn),所以沒有看過的同學(xué)建議還是先出門閱讀下
MVVM的本質(zhì)
首先 架構(gòu)設(shè)計更新到MVVM,是在MVC MVP KVC KVO的基礎(chǔ)上發(fā)展而來的,我計劃用前面的內(nèi)容來成就MVVM,而不會增加新的內(nèi)容,更不會平白無故的增加額外的管理類什么的
否則 前面幾章鋪陳就變得毫無意義可言 那這篇文章就如同行尸走肉一般 而我就是全身腐化氣質(zhì)了,我并不想那樣
可能人各有偏好,我不理解的是 我看過一些別人寫的MVVM,個人覺得沒有特別適合我的,不知道是block炫技 還是秀delegate,思想我是get到了,可代碼凌亂不堪,我考慮的是 我用這樣的設(shè)計,怎么跟別人協(xié)作項(xiàng)目,我心里是沒信息
爭取我在此篇文章里闡述的能使我自己不迷糊,如能觸碰到你心里的點(diǎn),這篇文章的價值才出得來
先看一張圖

這是我看到的一個利用MVVM的項(xiàng)目,我整理了下設(shè)計流程
圖中的意思我簡單概括下
一個vc創(chuàng)造一個manager,manager呢功能很強(qiáng),因?yàn)樽駨?個協(xié)議,也就是把控制權(quán)交給了manager,但是這樣的操作跟VC本身做又有什么區(qū)別
manager通過viewModel作為delegate 調(diào)度viewModel去管理view
viewModel又通過 manager遵從協(xié)議 驅(qū)動一些其他類,而且還是關(guān)聯(lián)對象存儲
viewMode又通過數(shù)據(jù)更新渲染view
。。。。。
反正 我看到毛毛多的block,block泛濫,不是排斥,只是這種block爆發(fā)蔓延,看上去挺高大上,反正很快要抓住主干思想脈絡(luò) 我是做不到的
我按照這樣的架構(gòu)設(shè)計去安排我的代碼,圖中的很多線我都需要考慮,不得不考慮,環(huán)節(jié)對不上,我的視圖可能就刷新不起來;
而且很多關(guān)系是靠代碼邏輯演化出來的,也就是我沒辦法從頂層設(shè)計上確切知道我未來的代碼走勢,復(fù)雜度會演化成什么樣子,我都不知道
我自己沒那個信心 能踐行得很好,從項(xiàng)目角度上講,我自己在這個架構(gòu)設(shè)計下,是沒有把控能力的
總之 我覺得是很亂,如果讓我選,我毫不猶豫選擇我之前設(shè)計的那個MVC,比這個好用,功能也不弱,協(xié)作起來問題也不大
回到正題
回到從MVVM最本質(zhì)的思想

這張圖的意思很明了,就兩根線
一根 View <-> viewModel
一根 viewModel <-> Model
如果這篇文章里我們能解決這兩根線,基本上就算實(shí)現(xiàn)了MVVM
除此之外,為了達(dá)到這個目的,我們的其他嫁接配置部分代碼不應(yīng)該侵入這兩條線
這個思想很純粹,很精辟 (ViewModel可以理解為一個大圣人,無所不能)
視圖通過 ViewModel來渲染 不管是主動的還是被動的,反正就一條線;
視圖的交互狀態(tài)提交給 ViewModel;
Model通過ViewModel來交付;
Model本身的增刪改 同步給ViewModel
接下來 我會按照上面的兩條線的解讀來設(shè)計
MVP的底子

會不會有人有疑問呀,這是騙人的吧?????
那么多代碼,就這幾條線,糊弄的吧。。。。
我相信會有人在心里這樣犯嘀咕
因?yàn)楹芏喙ぷ靼ㄅ渲檬窃诨惓跏蓟型瓿傻?,所以我畫了兩條虛線,VC構(gòu)建了兩個重要的角色,View和Presenter
而這些基類跟配置屬于萬年不變的類,它的配置調(diào)試只屬于架構(gòu)者,項(xiàng)目定式后,可以選擇作為庫,項(xiàng)目中的其他成員是不涉及的,剩下的工作就是依賴那兩條實(shí)線了
那對比一下上面MVVM的那個思想圖
我把最右側(cè)的那條
彎彎虛線變成實(shí)線,給View添加觀察者Adapter,
給 Adapter添加觀察者 Presenter,
這樣就變成兩條 雙向的線,
而且我把Adapter換成一個神奇名字 -- ViewModel
Presenter可以換成 Model,不過從職能上來將,我還是覺得Presenter更合適
我就基本上把之前的MVP改造成了一個MVVM架構(gòu),
更確切的說,是 PVVM, P就是Presenter
這可能么,我倒不存什么疑問,最起碼我是按照MVVM的設(shè)計思想來調(diào)整的我的MVP,不覺得有什么沖突的地方
從MVP處拷貝一份代碼出來


首先這份mvp架構(gòu)設(shè)計的代碼是沒問題的,只是我把關(guān)鍵字 list 替換成了 list1
接下來就是我們魔改了
adapter我打算沿用,名字不做更改,主要還是為了方便對比 MVP
給圖方便描述修改過程,我把MVP原理圖上的線給編上號

MVP調(diào)整過程
去掉線3

分兩步


presenter驅(qū)動view刷新 變成了 adapter的責(zé)任

現(xiàn)在代碼 按照圖中 線的變化 調(diào)整,去掉線3,增加線6
由于線3去掉了,則View的渲染驅(qū)動就沒有了,此消彼長,在別處也就是adapter補(bǔ)上



你會發(fā)現(xiàn)在adapter中 一句關(guān)鍵代碼 [self needRefresh] View刷新渲染數(shù)據(jù)
目的是達(dá)到了,有點(diǎn)粗糙
可以先看下原因

其實(shí)就是View作為adapter的觀察者,被動刷新渲染

MVP的架構(gòu)設(shè)計只改了一點(diǎn)點(diǎn),目前列表數(shù)據(jù)加載都是正常,加減操作 邏輯沒有受任何影響
添加View -> Adapter 的一條實(shí)線
線5我畫的是虛線,所以不用管它

不用改代碼,線自然就補(bǔ)充上來了,就相當(dāng)于 View觸發(fā) 執(zhí)行了一個同步流程,View也就拿到了返回值,刷新
這條線的添加就不用改代碼了
順便 Adapter -〉 Presenter 那條線也就有了
由此,MVVM也就改好了,我更習(xí)慣稱之為 VAP, 也就是 View - Adapter - Presenter
Adapter 就是ViewModel Presenter就是Model
這種設(shè)計就是個雙向的管道, 一頭是View,一頭是Presenter
Presenter是數(shù)據(jù)的源頭,自然而然從Presenter流入View,期間經(jīng)過Adapter
View最終是要把數(shù)據(jù)渲染到視圖上去的,狀態(tài)的變化自然要回溯到源頭Presenter,中間也經(jīng)過Adapter
比預(yù)想到修改要少好多好多,因?yàn)镸VP中有一個context的設(shè)計,所以目前這種架構(gòu)設(shè)計還可以再調(diào)整,
得益于靈活的context設(shè)計
你會否覺得與你預(yù)想的不一樣,感覺文章就沒說頭,反正對我而言,設(shè)計就這些,更復(fù)雜的代碼在此基礎(chǔ)上 不會變得煩亂不可控,有凌亂的也只會是在局部,不會在整個設(shè)計中蔓延開來
我平時就是這樣來控制的,不是說細(xì)節(jié)做到盡善盡美,也有瑕疵的地方,但不影響大局
還有一部分沒拿出來說明,就是這個設(shè)計架構(gòu)本身有些業(yè)務(wù)代碼是來自工程編譯本身產(chǎn)生的,
那是因?yàn)樵谶@個設(shè)計里設(shè)定了一些規(guī)則,一些職能類的命名規(guī)范,還有功能流轉(zhuǎn)方式是可以被這個架構(gòu)設(shè)計嚴(yán)格限制的
這樣即便項(xiàng)目龐雜了,想堆代碼是不可行的,堆代碼只會影響自己的業(yè)務(wù)本身,不安規(guī)矩來的話,就會發(fā)現(xiàn)代碼根本是跑不通的
進(jìn)一步抽象 改名
代碼改到這個地步,感覺根一般見到的MVVM一點(diǎn)都不像,沒關(guān)系,我們繼續(xù)
抽象出一個baseview

- 把訂閱放到VC的配置階段

- View中去掉訂閱邏輯,通用的訂閱邏輯交由base

如此,View 與 ViewModel之間就從代碼層面上看出訂閱關(guān)系了
View與ViewModule的業(yè)務(wù)綁定

同樣 這些也是base處理的,View本身透明

View訂閱之后,剩下的就是
數(shù)據(jù)的整體刷新到來,響應(yīng)刷新
根據(jù)訂閱的自定義類型,局部處理
這里不選用block,是因?yàn)橛胋lock會在View里多了一些不必要的配置,不夠純粹,當(dāng)前的方式 只需要處理這兩個方法就可以了,而且換個模塊 方法名也是固定的,基本上View里的代碼個是就是固定的模板了
涉及的代碼部分,同樣可以從 github 獲取