最近看到很多文章在談論MVP或者MVVM模式的,但其實無論MVP還是MVVM都只是MVC模式的一種變種。而如果你對MVC的設計理念都還沒有理解透徹,那么即使換成MVP亦或MVVM也不可能讓你雜亂不堪的代碼突然變得清晰明了起來,模式絕不是救命的稻草,它只是一種表現形式,真正要學的其蘊含的思維方式。
什么才是MVC?
這是一個非新手都就會嗤之以鼻的問題,試問哪個程序猿不知道什么是MVC,但在此我希望大家先忘記之前對MVC的所有知識,很多時候學習的第一步就是承認自己的無知,這是一個多么重要的步驟,又是一個多么容易遺忘的步驟啊。包括我自己也是如此,經常因為固有思維而變得傲慢而不自知,今天我們就一起重頭來學習一下MVC的歷史。
對于MVC的概念我想沒人不知,但是大部分人其實并不知道MVC的概念其實不止一個,從縱向來看,它經過了歷史上很多的演進和變種;從橫向來看,它也有許許多多不同的細微差異。即使包括后來的MVP和MVVM也都只能算它的一個變種而已。
經典MVC模式
大家坐穩了,老司機要帶大家穿越時空回到1978年,來聽聽MVC模式的創始人挪威教授Trygve Reenskaug是怎么定義MVC模式的。
Models:
A Model is an active representation of an abstraction in the form of data in a computing system.
Views:
To any given Model there is attached one or more Views, each View being capable of showing one or more pictorial representations of the Model on the screen and on hardcopy. A View is also able to perform such operations upon the Model that is reasonabely associated with that View.
Controller:
A controller is the link between a user and the system. It provides the user with input by arranging for relevant views to present themselves in appropriate places on the screen. It provides means for user output by presenting the user with menus or other means of giving commands and data. The controller receives such user output, translates it into the appropriate messages and pass these messages on .to one or more of the views.
A controller should never supplement the views, it should for example never connect the views of nodes by drawing arrows between them.
Conversely, a view should never know about user input, such as mouse operations and keystrokes. It should always be possible to write a method in a controller that sends messages to views which exactly reproduce any sequence of user commands.
有興趣的同學可以去閱讀完整的論文 The original MVC reports。
這是最早期的MVC模式,其中三者的定義可以簡單的理解為:
- Model,負責的是數據,這里的“數據”不僅限于數據本身,還包括處理數據的邏輯。
- View,負責數據的表現形式,將數據及數據的變化呈現給用戶。
- Controller,負責用戶的輸入,將用戶的命令轉化成消息傳遞給model或view,是一個翻譯者。
注意先不要噴,在早期的MVC模式中,Controller的設計目的其實并不是為了隔離Model和View的,而后來這點才發現了變化。
Model & View
Model和View的關系,也可以分為兩種形式:
- push model:View在model上將自己注冊為數據的監聽者,model的數據發生變化時會發送通知,view接到通知后用新數據更新自己。
- pull model:View負責在它需要的時候調用model來獲取當前最新的數據。
但不管是那種模式,model對于view都是沒有感知的,push model是在數據變化的時候簡單的發送廣播,告之所有對該數據變化感興趣的監聽者,而pull model是view對model進行調用。因此在任何一種模式下,model都是不能直接操作view。
這種model-view模式也就是俗稱的觀察者模式,model是被觀察者,view是觀察者,當被觀察者發送變化的時候,通知注冊在它上面的所有觀察者。這樣設計帶來的另一個好處就在于多個view可以監聽同一個model的變化。
Controller & View
關于Controller和View的關系,Controller是綁定在View上面的,意思是用戶任何在View上的操作(例如點擊按鈕等)都會調用Controller上的一個回調方法。其實也意味著View是持有Controller的引用的,當用戶做相應操作時,是由View來調用合適的Controller方法的,而Controller對View的操作在早期概念中則不太明確。
Controller & Model
Controller是可以向Model直接通信的。例如用戶點擊了刪除按鈕,那么Controller將用戶的這個操作翻譯成“用戶需要刪除這條數據”的消息傳遞給Model,Model負責刪除數據,然后通知View來更新頁面以告知用戶數據的變化。
現代MVC模式
與經典MVC模式不同,很多現代系統設計中,如Apple Cocoa框架,最大的改變在于將Controller的位置放在了Model和View之間。
主要區別就在于Controller的位置變了:Controller將消息傳遞給Model,M處理完數據后是先將數據的變化通知給C,再由C來通知View來變化視圖的。也就是說Controller變成了在Model和View之間雙向傳遞數據的中間協調者,關系變成了: View <-> Controller <-> Model 。
Model & View
Model和View之間沒有了任何關系,所有通信都是通過Controller傳遞。
Model & Controller & View
View通過Controller向model傳遞用戶操作的消息,而model在處理完數據后通過Controller來向View來傳遞結果。Controller從經典MVC模式中的單向翻譯官變成了雙向的中間人。
為什么要這樣變?
其實大家都看出來了,這樣變的主要目的就是為了讓Model和View之間不再直接聯系。從而使得三者的關系理得更清楚了。
這貨不就是MVP嗎?
了解MVP概念的同學可能讀到這里可以會產生巨大的問號:這貨不就是MVP嗎?MVP里面的Presenter不就是充當Controller和View之間的中間人嗎?
可以說Apple Cocoa這類框架使用的這種進化版的MVC確實離MVP相差很小了,可以說只差一步而已,MVP只是把三者之間的關系解藕得更厲害而已。
最后說兩句
不管是傳統的MVC,還是進化的MVC,亦或者MVP和MVVM模式,你會發現其實它們的設計理念都是一致的,逐步進化也只是為了更進一步的達到這個理念的目標,那就是:
上帝的歸上帝,凱撒的歸凱撒!
如果你沒有理解這一點,那么用什么模式也是混亂的;而如果你理解了這一點,什么模式不用也是清晰的。