混亂的MVC架構(gòu)
關(guān)于MVC,唯一明確的是分為Model、 View 和Controller三個(gè)部分,模型層可以單獨(dú)工作。至于他們之間的耦合,不同的平臺(tái)有不同的解釋。
ASP.NET MVC

我們可以簡單的理解為控制器負(fù)責(zé)管理視圖和模型;視圖負(fù)責(zé)展示模型中的內(nèi)容。
Spring MVC
更加復(fù)雜

通過了DispatchServlet將視圖層和控制器層完全解耦
iOS MVC
iOS 客戶端中的 Cocoa Touch 自古以來就遵循 MVC 架構(gòu)模式,不過 Cocoa Touch 中的 MVC 與 ASP.NET 和 Spring 中的 MVC 截然不同。

在iOS中,由于UIVIewController類持有一個(gè)根視圖UIVIew,所以視圖層和控制器層緊密耦合。這也是iOS視圖控制器非常臃腫重要原因之一。
Rails MVC

視圖層和模型層沒有直接耦合,而是通過控制器的作為中間人對(duì)信息進(jìn)行傳遞。
這種MVC的設(shè)計(jì),分離了視圖層和模型層的耦合,作為承擔(dān)數(shù)據(jù)存儲(chǔ)功能的模型層,可以通過控制器同時(shí)為不同的視圖提供數(shù)據(jù)。
控制器根據(jù)用戶發(fā)出的HTTP請(qǐng)求,從模型中取出相同的數(shù)據(jù),然后傳遞給不同的視圖渲染出不同的結(jié)果。
標(biāo)準(zhǔn)的MVC
沒有一個(gè)確切的答案,多個(gè)框架到MVC的理解有一點(diǎn)完全相同,就是講整個(gè)應(yīng)用分為Model、View 和 Controller 三個(gè)部分。
作者理解的標(biāo)準(zhǔn)的MVC模型和ASP.NET 中對(duì)于 MVC的設(shè)計(jì)相同:
- 控制器負(fù)責(zé)對(duì)模型中的數(shù)據(jù)進(jìn)行更新,而視圖向模型中請(qǐng)求數(shù)據(jù)
- 當(dāng)用戶的行為觸發(fā)操作的時(shí)候,會(huì)有控制器更新模型,并通知視圖進(jìn)行更新,在這時(shí)視圖向模型請(qǐng)求新的數(shù)據(jù)
依賴關(guān)系
模型層可以單獨(dú)工作,而控制器和模型層都依賴模型層中的數(shù)據(jù)。
分離展示層

MVC最重要的概念就是分離展示層(Separated Presentation),如何在領(lǐng)域?qū)ο螅―omain Object)和我們在屏幕上看到的GUI元素進(jìn)行劃分是MVC架構(gòu)模式最核心的問題。
GUI 應(yīng)用程序由于其需要展示內(nèi)容的特點(diǎn),分為兩個(gè)部分:一部分是用于展示內(nèi)容的展示層(Presentation Layer),另一部分包含領(lǐng)域和數(shù)據(jù)邏輯的領(lǐng)域?qū)樱―omain Layer)。
展示層依賴于領(lǐng)域?qū)又写鎯?chǔ)的數(shù)據(jù),而領(lǐng)域?qū)訉?duì)于展示層一無所知,領(lǐng)域?qū)悠鋵?shí)也是 MVC 模式中的模型層,而展示層可以理解為 VC 部分。
MVC 最重要的目的并不是規(guī)定各個(gè)模塊應(yīng)該如何交互和聯(lián)系,而是將原有的混亂的應(yīng)用程序劃分出合理的層級(jí),把一團(tuán)混亂的代碼,按照展示層和領(lǐng)域?qū)臃殖蓛蓚€(gè)部分;在這時(shí),領(lǐng)域?qū)又械念I(lǐng)域?qū)ο笥捎谄渥陨硖攸c(diǎn)不需要對(duì)展示層有任何了解,可以同時(shí)為不同的展示層工作。
觀察者同步
在 MVC 模式中,模型可以單獨(dú)工作,同時(shí)它對(duì)使用模型中數(shù)據(jù)的視圖和控制器一無所知,為了保持模型的獨(dú)立性,我們需要一種機(jī)制,當(dāng)模型發(fā)生改變時(shí),能夠同時(shí)更新多個(gè)視圖和控制器的內(nèi)容;在這時(shí),就需要以觀察者同步的方式解決這個(gè)問題。

我們將所有需要實(shí)時(shí)更新的組件注冊成為模型的觀察者,在模型的屬性發(fā)生變化時(shí),通過觀察者模式推送給所有注冊的觀察者(視圖和控制器)
當(dāng)多個(gè)視圖共享相同的數(shù)據(jù)時(shí),觀察者同步是一個(gè)非常關(guān)鍵的模式,它能夠在對(duì)這些視圖不知情的前提下,同時(shí)通知多個(gè)視圖;通過觀察者模式,我們可以非常容易地創(chuàng)建一個(gè)依賴于同一模型的視圖。
占主導(dǎo)地位的控制器
MVC 架構(gòu)模式的三個(gè)組成部分:Model、View 和 Controller 中最重要的就是控制器,它承擔(dān)了整個(gè)架構(gòu)中的大部分業(yè)務(wù)邏輯,同時(shí)在用戶請(qǐng)求到達(dá)或者事件發(fā)生時(shí)都會(huì)首先通知控制器并由它來決定如何響應(yīng)這次請(qǐng)求或者事件。

在 MVC 中,所有的用戶請(qǐng)求都會(huì)首先交給控制器,再由控制器來決定如何響應(yīng)用戶的輸入,無論是更新模型中的信息還是渲染相應(yīng)的視圖,都是通過控制器來決定的;也就是說,在 MVC 中,控制器占據(jù)主導(dǎo)地位,它決定用戶的輸入是如何被處理的。
被動(dòng)的模型
在絕大多數(shù)的 MVC 架構(gòu)模式中,模型都不會(huì)主動(dòng)向視圖或者控制器推送消息;模型都是被動(dòng)的,它只存儲(chǔ)整個(gè)應(yīng)用中的數(shù)據(jù),而信息的獲取和更新都是由控制器來驅(qū)動(dòng)的。

但是當(dāng)模型中的數(shù)據(jù)發(fā)生變化時(shí),卻需要通過一些方式通知對(duì)應(yīng)的視圖進(jìn)行更新,在這種情況下其實(shí)也不需要模型主動(dòng)將數(shù)據(jù)變化的消息推送給視圖;因?yàn)樗袑?duì)于模型層的改變都是由用戶的操作導(dǎo)致的,而用戶的操作都是通過控制器來處理的,所以只需要在控制器改變模型時(shí),將更新的信息發(fā)送給視圖就可以了;當(dāng)然,我們也可以通過觀察者模式向未知的觀察者發(fā)送通知,以保證狀態(tài)在不同模塊之間能夠保持同步。
MVP
MVC 與 MVP 之間的區(qū)別其實(shí)并不明顯,作者認(rèn)為兩者之間最大的區(qū)別就是 MVP 中使用 Presenter 對(duì)視圖和模型進(jìn)行了解耦,它們彼此都對(duì)對(duì)方一無所知,溝通都通過 Presenter 進(jìn)行。

在 MVP 中,Presenter 可以理解為松散的控制器,其中包含了視圖的 UI 業(yè)務(wù)邏輯,所有從視圖發(fā)出的事件,都會(huì)通過代理給 Presenter 進(jìn)行處理;同時(shí),Presenter 也通過視圖暴露的接口與其進(jìn)行通信。
目前常見的 MVP 架構(gòu)模式其實(shí)都是它的變種:Passive View 和 Supervising Controller,接下來的內(nèi)容也是圍繞這兩種變種進(jìn)行展開的。
被動(dòng)視圖Passive View

MVP 的第一個(gè)主要變種就是被動(dòng)視圖(Passive View);顧名思義,在該變種的架構(gòu)模式中,視圖層是被動(dòng)的,它本身不會(huì)改變自己的任何的狀態(tài),所有的狀態(tài)都是通過 Presenter 來間接改變的。
被動(dòng)的視圖層就像前端中的 HTML 和 CSS 代碼,只負(fù)責(zé)展示視圖的結(jié)構(gòu)和內(nèi)容,本身不具有任何的邏輯.
視圖成為了完全被動(dòng)的并且不再根據(jù)模型來更新視圖本身的內(nèi)容,也就是說,不同于 MVC 中的依賴關(guān)系;在被動(dòng)視圖中,視圖層對(duì)于模型層沒有任何的依賴:

通信方式

- 當(dāng)視圖接收到來自用戶的事件時(shí),會(huì)將事件轉(zhuǎn)交給 Presenter 進(jìn)行處理;
- 被動(dòng)的視圖向外界暴露接口,當(dāng)需要更新視圖時(shí) Presenter 通過視圖暴露的接口更新視圖的內(nèi)容;
- Presenter 負(fù)責(zé)對(duì)模型進(jìn)行操作和更新,在需要時(shí)取出其中存儲(chǔ)的信息;
- 當(dāng)模型層改變時(shí),可以將改變的信息發(fā)送給觀察者 Presenter;
在 MVP 的變種被動(dòng)視圖中,模型的操作以及視圖的更新都僅通過 Presenter 作為中間人進(jìn)行。
監(jiān)督控制器Supervising Controller
與被動(dòng)視圖中狀態(tài)同步都需要顯式的操作不同,監(jiān)督控制器(Supervising Controller)就將部分需要顯式同步的操作變成了隱式的:

在監(jiān)督控制器中,視圖層接管了一部分視圖邏輯,主要內(nèi)容就是同步簡單的視圖和模型的狀態(tài);而監(jiān)督控制器就需要負(fù)責(zé)響應(yīng)用戶的輸入以及一部分更加復(fù)雜的視圖、模型狀態(tài)同步工作。
對(duì)于用戶輸入的處理,監(jiān)督控制器的做法與標(biāo)準(zhǔn) MVP 中的 Presenter 完全相同;但是對(duì)于視圖、模型的同步工作,監(jiān)督控制器會(huì)盡可能地將所有簡單的屬性以數(shù)據(jù)綁定的形式聲明在視圖層中,類似于 Vue 中雙向綁定的簡化版本
通信方式
監(jiān)督控制器中的視圖和模型層之間增加了兩者之間的耦合,也就增加了整個(gè)架構(gòu)的復(fù)雜性:

視圖和監(jiān)督控制器、模型與監(jiān)督控制器的關(guān)系與被動(dòng)視圖中兩者與 Presenter 的關(guān)系幾乎相同,視圖和模型之間新增的依賴就是數(shù)據(jù)綁定的產(chǎn)物;視圖通過聲明式的語法與模型中的簡單屬性進(jìn)行綁定,當(dāng)模型發(fā)生改變時(shí),會(huì)通知其觀察者視圖作出相應(yīng)的更新。
通過這種方式能夠減輕監(jiān)督控制器的負(fù)擔(dān),減少其中簡單的代碼,將一部分邏輯交由視圖進(jìn)行處理;這樣也就導(dǎo)致了視圖同時(shí)可以被 Presenter 和數(shù)據(jù)綁定兩種方式更新,相比于被動(dòng)視圖,監(jiān)督控制器的方式也降低了視圖的可測試性和封裝性。
占主導(dǎo)地位的視圖
MVP 架構(gòu)模式中,視圖層在整個(gè)架構(gòu)中都是占主導(dǎo)地位的:

MVC 與MVP的區(qū)別:
- 在 MVC 中,控制器負(fù)責(zé)以不同的視圖響應(yīng)客戶端請(qǐng)求的不同動(dòng)作;然而,不同于 MVC 模式,MVP 中視圖將所有的動(dòng)作交給 Presenter 進(jìn)行處理;
- MVC 中的所有的動(dòng)作都對(duì)應(yīng)著一個(gè)控制器的方法調(diào)用,Web 應(yīng)用中的每一個(gè)動(dòng)作都是對(duì)某一個(gè) URL 進(jìn)行的操作,控制器根據(jù)訪問的路由和方法(GET 等)對(duì)數(shù)據(jù)進(jìn)行操作,最終選擇正確的視圖進(jìn)行返回。
- MVC 中控制器返回的視圖沒有直接綁定到模型上,它僅僅被控制器渲染并且是完全無狀態(tài)的,其中不包含任何的邏輯,但是 MVP 中的視圖必須要將對(duì)應(yīng)的事件代理給 Presenter 執(zhí)行,否則事件就無法被響應(yīng)。
- 另一個(gè) MVP 與 MVC 之間的重大區(qū)別就是,MVP(Passive View)中的視圖和模型是完全解耦的,它們對(duì)于對(duì)方的存在完全不知情。
MVVM
MVVM的演變 從展示模型到視圖模型
早在 2004 年,Martin Fowler 發(fā)表了一篇名為 Presentation Model (以下簡稱為 PM 模式)的文章,PM 模式與 MVP 比較相似,它從視圖層中分離了行為和狀態(tài);PM 模式中創(chuàng)建了一個(gè)視圖的抽象,叫做 Presentation Model,而視圖也成為了這個(gè)模型的『渲染』結(jié)果。
2005 年,John Gossman 在他的博客上公布了 Introduction to Model/View/ViewModel pattern for building WPF apps 一文。MVVM 與 Martin Fowler 所說的 PM 模式其實(shí)是完全相同的,F(xiàn)owler 提出的 PM 模式是一種與平臺(tái)無關(guān)的創(chuàng)建視圖抽象的方法,而 Gossman 的 MVVM 是專門用于 WPF 框架來簡化用戶界面的創(chuàng)建的模式;我們可以認(rèn)為 MVVM 是在 WPF 平臺(tái)上對(duì)于 PM 模式的實(shí)現(xiàn)。
Presentation Model
PM 模式將視圖中的全部狀態(tài)和行為放到一個(gè)單獨(dú)的展示模型中,協(xié)調(diào)領(lǐng)域?qū)ο螅P停┎⑶覟橐晥D層提供一個(gè)接口。
在監(jiān)督控制器中,視圖層與模型層中的一些簡單屬性進(jìn)行綁定,在模型屬性變化時(shí)直接更新視圖,而 PM 通過引入展示模型將模型層中的數(shù)據(jù)與復(fù)雜的業(yè)務(wù)邏輯封裝成屬性與簡單的數(shù)據(jù)同時(shí)暴露給視圖,讓視圖和展示模型中的屬性進(jìn)行同步。

展示模型中包含所有的視圖渲染需要的動(dòng)態(tài)信息,包括視圖的內(nèi)容(text、color)、組件是否啟用(enable),除此之外還會(huì)將一些方法暴露給視圖用于某些事件的響應(yīng)。
在 PM 模式中,同一個(gè)展示模型可以與多個(gè)領(lǐng)域?qū)ο蠼换?,多個(gè)視圖可以使用相同的展示模型,但是每一個(gè)視圖只能持有一個(gè)展示模型。

MVVM就是對(duì)PM模式的一種實(shí)現(xiàn)
WPF 作為微軟用于處理 GUI 軟件的框架,提供了一套非常優(yōu)雅的解決方案。

從 Model-View-ViewModel 這個(gè)名字來看,它由三個(gè)部分組成,也就是 Model、View 和 ViewModel;其中視圖模型(ViewModel)其實(shí)就是 PM 模式中的展示模型,在 MVVM 中叫做視圖模型。
除了我們非常熟悉的 Model、View 和 ViewModel 這三個(gè)部分,在 MVVM 的實(shí)現(xiàn)中,還引入了隱式的一個(gè) Binder 層,而聲明式的數(shù)據(jù)和命令的綁定在 MVVM 模式中就是通過它完成的。

在iOS上,綁定機(jī)制可以使用如下兩個(gè)方案:
- 基于KVO的綁定庫如 RZDataBinding 和 SwiftBond
- 完全的函數(shù)響應(yīng)式編程,比如像ReactiveCocoa、RxSwift或者 PromiseKit
無論是 MVVM 還是 Presentation Model,其中最重要的不是如何同步視圖和展示模型/視圖模型之間的狀態(tài),是使用觀察者模式、雙向綁定還是其它的機(jī)制都不是整個(gè)模式中最重要的部分,最為關(guān)鍵的是展示模型/視圖模型創(chuàng)建了一個(gè)視圖的抽象,將視圖中的狀態(tài)和行為抽離出一個(gè)新的抽象,這才是 MVVM 和 PM 中需要注意的。
從 MVC 架構(gòu)模式到 MVVM,從分離展示層到展示模型層,經(jīng)過幾十年的發(fā)展和演變,MVC 架構(gòu)模式出現(xiàn)了各種各樣的變種,并在不同的平臺(tái)上有著自己的實(shí)現(xiàn)。
參考鏈接: