淺談框架模式 MVC、MVP 和 MVVM

skiner發表於2016-12-05

前言:框架模式不是一門寫程式碼的學問,而是一門管理與組織程式碼的學問。其本質是一種軟體開發的模型。與設計模式不同,設計模式是在解決一類問題時總結抽象出的公共方法(工廠模式,介面卡模式,單例模式,觀察者模式……),他們與某種具體的技術棧無關。一種框架模式往往使用了多種設計模式,切不要把他們的關係搞混。

更多資訊可以看看這本《Developing Backbone Application》

一代目: 指令碼式設計(無架構設計):

1

下面這樣的程式碼,就是無任何設計模式的產物:

 

嗯嗯~先別吐槽,因為這種編碼方式還是有他的優點的。

短短的幾行程式碼,包含了建立,樣式,繫結,插入。 balabala。。。。。

這種搞法雖有不少缺點,但麻雀雖小五臟俱全,所有功能一應俱全。早些年由於UI程式還處在一個懵懂期, 邏輯不算太複雜,程式碼量也不會太多。這樣的搞法似乎也沒有什麼問題。 畢竟到達A B兩點最短的距離就是直線上述程式碼可以說是實現某功能的最短路徑 典型的例子就是 ASM (雖然組合語言不是用來寫UI的),他們共有的缺點是 :入口單一 功能簡單 不可維護

讓我們修改一下上面的邏輯:

 

這裡我們將一個功能拆分成了3個部分,即 外觀 事件 和屬性。

函式將他們重新分離成一個個獨立的邏輯塊,這樣一定程度上達到了分離複用的目的,比如你想修改外觀,就去doCss函式裡去找。。。

就像有錢人追求更多的財富,權貴追求更多權利一樣。

人類總在思考同一個問題, 我們能不能做得更好。。

直到有一天。。。

二代目: 程式碼檔案分離(CodeBehind):

2

對asp。net尤其是webForm,winForm模式熟悉的同學,肯定對aspx 和 aspx。cs 這2種檔案非常瞭解。

0    0-0

aspx是檢視檔案,而對應的。cs檔案是他的相關邏輯處理檔案 事件驅動模式下,框架幫我們完成了基本的事件型別,我們要做的是在事件下完成相關業務邏輯。

在前端中,也可以找到二代目大人的影分身。。

 

各檔案各司其職,編寫的時候分離,在執行的時候合併。這樣進一步降低了功能之間的耦合度。 檢視看起來非常”清爽”,對應的邏輯也被分離成一個個檔案, 交由相應的開發人員處理。

如果你涉獵的技術範圍很廣,你會發現其實二代目大人已經出現在諸多成熟的技術棧中。。。。

但是沒過多久伊甸園歡樂的笑聲被下面這個需求打破。。。

q

為了實現他,在這裡我只寫虛擬碼了:

 

這裡比較困難的是 第2步

就是如果顯示蘋果數的控制元件是另一個程式設計師開發的黑盒, 如何修改其值?

於是程式設計師A 去找 程式設計師B 尋求是否存在對應的 get/set 方法。

程式設計師B說, “有” 字還沒落地, 開發買梨的程式設計師C 又踹門進來了,問了同樣的問題, 後來才知道 開發吃蘋果功能的程式設計師D正在路上。。。

於是程式設計師B 不得不把介面的詳細資訊寫到 wiki中, 於是 程式設計師CDEFGHIJKMLN 都看了wiki 懂了。

完成這件事 程式設計師B 寫wiki 花了 10分鐘, 程式設計師CDEFGHIJKMLN 看wiki每人花 1分鐘,一共團隊成本 20分鐘。

於是 wiki中這個 get/set 介面函式出現在了每一個被繫結的函式裡一共4個button 出現4次

ps: 這個get/set介面本質上就是一個view重新整理介面

事情仍在在醞釀:

產品大爺發話了,要改成下面這樣:

q1

多了一個求和。

於是虛擬碼變成了這樣:

這次 程式設計師B為了人身安全,提前把 get/set 介面釋出到了 wiki上。。

於是 總和控制元件的值 = 總和變數 這行程式碼出現了 4次。

產品存在的意義就是將程式狗虐到極致:qq

於是需求改成了這樣:

q2

只是刪了一行。

於是 4個函式中 所有相關程式碼都被刪除。。。 共影響 4行程式碼。。

段子講完了其核心問題在於按照事件進行的業務模組劃分有時候是不合理的事件是使用者行為的入口但不是程式邏輯的入口 一個button的click就可能橫跨N個領域 需要N個人來進行協作 這部分邏輯到最後還是會耦合在一起通過各種函式封裝進行解耦無疑是揚湯止沸而我們需要的是釜底抽薪

三代目: M-V-C:(模型檢視控制器)

3

現在網上有很多關於mvc的介紹,讓人糾結的是他們各不相同,而且有的根本就說的不對, 對於框架模式這東西,沒有一個嚴格的規定說這樣搞是 mvc 那樣就不是。 甚至連mvc本身也有很多變種,我們只要從根源上理解這個東西就行。

我就不扒祖墳了,我們們只需要知道它已經存在了 30多年就行了。

我們思考一下 UI(圖形化使用者介面) 的本質:

為什麼要有UI, 在計算機眼中 一切即資料,其實要是深挖這個問題,資料與操作其實都是 0 1 組成的機器碼,只不過 CPU執行的時候用指定暫存器的資料當做指令罷了,也就是說 決定一個資料到底是資料還是指令 只取決於他所在的暫存器位置。(好了好了 扯遠了,往回跑。。) 資料的操作是抽象的,是專業人士乾的事情, 計算機為了走進千家萬戶, 必須提供一種傻瓜式的操作方式,於是UI誕生了。。。 用一句話解釋UI就是:他是資料到影象的一種對映程式;

剛才說了它是一種對映程式,使用者通過操作影象上的按鈕,來達到運算元據的目的,資料被使用者改變後,肯定需要從新生成對映。

請允許我向上一張老掉牙的圖:

mvc

說說這裡面 Model View Controller 是幹什麼的:

1.View: 放置檢視相關的程式碼,原則上裡面不應該有任何業務邏輯。

2.Controller: 放置檢視與模型之間的對映,原則上這裡應該很薄,他只放一些事件繫結相關的程式碼(router),但並不實現真正的功能,他只是一個橋樑。

3.Model: 這裡的model不是說 實體類, 他是主要實現邏輯的地方。

那還是上面 買水果的例子,那麼在MVC下該如何設計呢:

概念 解釋
view層 放置介面程式碼,以及一些重新整理邏輯 如資料中的 0 1 轉成 男 女
controller層 放置一些繫結邏輯。完成router,不實現函式體。
model層 接收view的註冊,當自身資料變化時,執行view的重新整理函式。 業務邏輯都在這裡

他是這樣一個流程:

1.建立顯示蘋果數量的控制元件。

2.將上面控制元件註冊到model中。(設定關聯的資料,–蘋果數變數)

3.修改model中 蘋果數變數 。

4.由於蘋果數變數被修改,觸發所有繫結在上面的控制元件(view)從新執行重新整理函式。

5.顯示蘋果數量的控制元件被更新。

這樣便解決了大部分介面與邏輯耦合的問題但是它並不完美:

View 和 Model 並不是完全脫離的,還是有一些邏輯耦合,因為需要根據修改後的model從新重新整理view。 難免view裡面沾染一點model的結構。

程式碼量膨脹。

不方便進行更精細的顆粒化控制。(因為view只知道 model被改了,但不知道誰改的!)

  1. model在對應多個view的時候,很難都伺候到位。

於是。。。

四代目: M-V-P:(模型檢視派發器)

4

請允許我再上一張老圖:

mvp

針對mvc的一些問題,在mvp模式下, 斬斷了 view 與 model的關係, 當m 改變時,m 通知 p 去改變v, 所以v變得更純潔(重新整理邏輯被移動到了p層), 為了保證m可以最大程度的複用 一部分業務邏輯也從 m 轉移到了p所以 mvp下 p 非常厚實。

mvp中最後改變v的是p那麼在 v與p 之間會有一個介面,解決怎麼轉換以及傳值的問題。

五代目: M-V-VM:(模型檢視抽象檢視)

MVVM最早來自於微軟社群用於WPF

5

Model-View-ViewModel的關係圖:

mvvm

mvvm 與 mvp 的最大區別就是它使用 資料繫結(Data Binding)、依賴屬性(Dependency Property)、命令(Command)、路由事件(Routed Event) 來搞定與view層的互動, 但是這種繫結是與某種具體技術棧相關的, ViewModel從Model中抽象而來,但更貼近於業務模型, 比如你Model中某欄位是 true false, ViewModel中可能就是 “黑”,”白”等 這種更貼近業務場景的描述。 ViewModel中的屬性直接與某具體控制元件的屬性相繫結。 也就是說當某具體控制元件發生變化,ViewModel中的 某個欄位就會跟著變化,然後Model中的欄位也會進一步變化。

mvvm0

以上述為例:

使用者使用UI修改了性別欄位:

1.操作觸發繫結在UI上的事件(Data Binding 自動完成)

2.事件進入vm層,根據繫結規則,找出對應的vm欄位, (如表示性別的元件繫結的是vm中的sex欄位)

3.vm上的sex被設定成true,(view層上值為”男” “女”,但是在抽象的vm層中我們用 bool 來表示這個欄位)

4.同理尋找m層上的對應欄位,m上的sex被vm修改成1

5.m找到所有與sex欄位有繫結關係的vm通知他們更新。

6.所有接到通知的vm更新sex欄位。

7.vm尋找所有與sex欄位有繫結關係的view層控制元件,通知他們更新(Data Binding 自動完成)。

8.view被更新。所有涉及到sex欄位的元件都被重新整理。

有時候這個流程未必是從 1 步開始,如果直接對 m 進行修改,則就是從第 4 步開始的。

同理如果沒有view層,則沒有必要進行 7 , 8步驟。

這就是說 mvvm 下可以完全乾掉 view 層, 方便的進行自動化測試。

小結 (推送/訂閱 這個是資料驅動的核心)

不管是 mvc 還是 mvp 或 mvvm ,他們都是 資料驅動 的。核心上基於 m 推送訊息v或p來訂閱 這個模型。使用者需要維護的不再是 UI 樹,而是抽象的資料。(通過資料,可以隨時構建出新的 UI 樹), 當 UI 的狀態一旦多起來,這種框架模式的優勢便體現出來了。 因為維護資料可比維護 UI 狀態爽多了。

前端中的mvc:

並不是說 m v c 三者一定要獨立出現才行,比如 Backbone。js 它的 controller 層只是一個 router。 其實在傳統 mvc 中 controller 裡本來就沒有太多的邏輯,他只是 一個事件的”傳遞者”, 加之 javascript 中人們習慣使用匿名函式當事件回撥,這樣就等於直接在 view 層中把功能函式實現了。 所以 view 與 controller 合併 或者 controller 與model 合併都有可能。

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

淺談框架模式 MVC、MVP 和 MVVM

相關文章