iOS架構(gòu)設(shè)計(三)- MVVM

我是不敢輕易談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),這篇文章的價值才出得來

先看一張圖

image.png

這是我看到的一個利用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ì)的思想

image.png

這張圖的意思很明了,就兩根線

  • 一根 View <-> viewModel

  • 一根 viewModel <-> Model

如果這篇文章里我們能解決這兩根線,基本上就算實(shí)現(xiàn)了MVVM

除此之外,為了達(dá)到這個目的,我們的其他嫁接配置部分代碼不應(yīng)該侵入這兩條線

這個思想很純粹,很精辟 (ViewModel可以理解為一個大圣人,無所不能)

  • 視圖通過 ViewModel來渲染 不管是主動的還是被動的,反正就一條線;

  • 視圖的交互狀態(tài)提交給 ViewModel;

  • Model通過ViewModel來交付;

  • Model本身的增刪改 同步給ViewModel

接下來 我會按照上面的兩條線的解讀來設(shè)計

MVP的底子

image.png

會不會有人有疑問呀,這是騙人的吧?????

那么多代碼,就這幾條線,糊弄的吧。。。。

我相信會有人在心里這樣犯嘀咕

因?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處拷貝一份代碼出來

image.png
image.png

首先這份mvp架構(gòu)設(shè)計的代碼是沒問題的,只是我把關(guān)鍵字 list 替換成了 list1
接下來就是我們魔改了

adapter我打算沿用,名字不做更改,主要還是為了方便對比 MVP

給圖方便描述修改過程,我把MVP原理圖上的線給編上號

image.png

MVP調(diào)整過程

去掉線3

image.png

分兩步

image.png

image.png

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

image.png

現(xiàn)在代碼 按照圖中 線的變化 調(diào)整,去掉線3,增加線6

由于線3去掉了,則View的渲染驅(qū)動就沒有了,此消彼長,在別處也就是adapter補(bǔ)上

image.png
image.png
image.png

你會發(fā)現(xiàn)在adapter中 一句關(guān)鍵代碼 [self needRefresh] View刷新渲染數(shù)據(jù)

目的是達(dá)到了,有點(diǎn)粗糙

可以先看下原因

image.png

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

image.png

MVP的架構(gòu)設(shè)計只改了一點(diǎn)點(diǎn),目前列表數(shù)據(jù)加載都是正常,加減操作 邏輯沒有受任何影響

添加View -> Adapter 的一條實(shí)線

線5我畫的是虛線,所以不用管它

image.png

不用改代碼,線自然就補(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

image.png
  • 把訂閱放到VC的配置階段
image.png
  • View中去掉訂閱邏輯,通用的訂閱邏輯交由base
image.png

如此,View 與 ViewModel之間就從代碼層面上看出訂閱關(guān)系了

View與ViewModule的業(yè)務(wù)綁定

image.png

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

image.png

View訂閱之后,剩下的就是

  • 數(shù)據(jù)的整體刷新到來,響應(yīng)刷新

  • 根據(jù)訂閱的自定義類型,局部處理

這里不選用block,是因?yàn)橛胋lock會在View里多了一些不必要的配置,不夠純粹,當(dāng)前的方式 只需要處理這兩個方法就可以了,而且換個模塊 方法名也是固定的,基本上View里的代碼個是就是固定的模板了

涉及的代碼部分,同樣可以從 github 獲取

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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