中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

淺談 MVC、MVP 和 MVVM 架構(gòu)模式

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬Linux鏡像隨意使用
  • 談?wù)?MVX 中的 Model
  • 談?wù)?MVX 中的 View
  • 談?wù)?MVX 中的 Controller
  • 淺談 MVC、MVP 和 MVVM 架構(gòu)模式

這是 MVX 系列的第四篇文章,在前面的文章中,我們先后介紹了 iOS 中的 Model、View 和 Controller 的現(xiàn)狀,對(duì)比了其他平臺(tái)中的設(shè)計(jì),最后給出了作者理想中的結(jié)構(gòu)。

而在這一篇文章中,作者會(huì)依次介紹 MVC、MVP 以及 MVVM 架構(gòu)模式以及不同平臺(tái)對(duì)它們的使用;雖然參考了諸多資料,不過文中觀點(diǎn)難免摻入作者的主觀意見,作者也希望文中的錯(cuò)誤與不足之處能被各位讀者指出。

前面的幾篇文章中重點(diǎn)都是介紹 iOS 平臺(tái)上的 Model、View 和 Controller 如何設(shè)計(jì),而這篇文章會(huì)對(duì)目前 GUI 應(yīng)用中的 MVC、MVP 和 MVVM 架構(gòu)模式進(jìn)行詳細(xì)地介紹。

MVC

在整個(gè) GUI 編程領(lǐng)域,MVC 已經(jīng)擁有將近 50 年的歷史了。早在幾十年前,Smalltalk-76 就對(duì) MVC 架構(gòu)模式進(jìn)行了實(shí)現(xiàn),在隨后的幾十年歷史中,MVC 產(chǎn)生了很多的變種,例如:HMVC、MVA、MVP、MVVM 和其它將 MVC 運(yùn)用于其它不同領(lǐng)域的模式。

早期的 MVC

而本文的內(nèi)容就是從 MVC 開始的,作為架構(gòu)模式中最出名并且應(yīng)用最廣泛的架構(gòu)模式,MVC 并沒有一個(gè) 明確的 定義,網(wǎng)上流傳的 MVC 架構(gòu)圖也是形態(tài)各異,作者查閱了很多資料也沒有辦法確定到底什么樣的架構(gòu)圖才是 標(biāo)準(zhǔn)的 MVC 實(shí)現(xiàn)。

設(shè)計(jì) MVC 的重要目的就是在人的心智模型與計(jì)算機(jī)的模型之間建立一個(gè)橋梁,而 MVC 能夠解決這一問題并 為用戶提供直接看到信息和操作信息的功能 。

更早的概念類似 Model-View-Editor(MVE)這里就不再提及了,感興趣的讀者可以閱讀這篇論文 Thing-Model-View-Editor 了解更多的信息。

混亂的 MVC 架構(gòu)

作者相信,稍有編程經(jīng)驗(yàn)的開發(fā)者就對(duì) MVC 有所了解,至少也是聽過 MVC 的名字。作者也一直都認(rèn)為絕大多數(shù)人對(duì)于 MVC 理解的概念都一樣,很多人對(duì)于 MVVM 的實(shí)現(xiàn)有很大爭(zhēng)論,說遵循什么什么架構(gòu)的是 MVVM,MVVM 有什么組件、沒有什么組件,而對(duì)于 MVC 仿佛沒有那么大的疑問,這其實(shí)卻不然。

ASP.NET MVC

在最近的幾個(gè)月,作者發(fā)現(xiàn)不同人對(duì)于 MVC 的理解有巨大的差異,這是 ASP.NET MVC Overview 一文中對(duì)于 MVC 模式描述的示意圖。

圖片中并沒有對(duì) Model、View 和 Controller 三者之間如何交互進(jìn)行說明,有的也只是幾個(gè)箭頭。我們應(yīng)該可以這么簡(jiǎn)單地理解:

  1. 控制器負(fù)責(zé)管理視圖和模型;
  2. 視圖負(fù)責(zé)展示模型中的內(nèi)容;

由于文章沒有明確對(duì)這三個(gè)箭頭的含義進(jìn)行解釋,所以在這里也僅作推斷,無法確認(rèn)原作者的意思。

Spring MVC

與 ASP.NET 不同,Spring MVC 對(duì)于 MVC 架構(gòu)模式的實(shí)現(xiàn)就更加復(fù)雜了,增加了一個(gè)用于分發(fā)請(qǐng)求、管理視圖的 DispatchServlet:

在這里不再介紹 Spring MVC 對(duì)于 HTTP 請(qǐng)求的處理流程,我們對(duì)其中 Model、View 和 Controller 之間的關(guān)系進(jìn)行簡(jiǎn)單的分析:

  1. 通過 DispatchServlet 將控制器層和視圖層完全解耦;
  2. 視圖層和模型層之間沒有直接關(guān)系,只有間接關(guān)系,通過控制器對(duì)模型進(jìn)行查詢、返回給 DispatchServlet 后再傳遞至視圖層;

雖然 Spring MVC 也聲稱自己遵循 MVC 架構(gòu)模式,但是這里的 MVC 架構(gòu)模式和 ASP.NET 中卻有很大的不同。

iOS MVC

iOS 客戶端中的 Cocoa Touch 自古以來就遵循 MVC 架構(gòu)模式,不過 Cocoa Touch 中的 MVC 與 ASP.NET 和 Spring 中的 MVC 截然不同。

在 iOS 中,由于 UIViewController 類持有一個(gè)根視圖 UIView ,所以視圖層與控制器層是緊密耦合在一起的,這也是 iOS 項(xiàng)目經(jīng)常遇到視圖控制器非常臃腫的重要原因之一。

Rails MVC

Rails 作為著名的 MVC 框架,視圖層和模型層沒有直接的耦合,而是通過控制器作為中間人對(duì)信息進(jìn)行傳遞:

這種 MVC 的設(shè)計(jì)分離了視圖層和模型層之間的耦合,作為承擔(dān)數(shù)據(jù)存儲(chǔ)功能的模型層,可以通過控制器同時(shí)為多種不同的視圖提供數(shù)據(jù):

控制器根據(jù)用戶發(fā)出的 HTTP 請(qǐng)求,從模型中取出相同的數(shù)據(jù),然后傳給不同的視圖以渲染出不同的結(jié)果。Rails 中的 MVC 架構(gòu)模式能夠很好地將用于展示的視圖和用于存儲(chǔ)數(shù)據(jù)的數(shù)據(jù)庫進(jìn)行分離,兩者之間通過控制器解耦,能夠?qū)崿F(xiàn)同一數(shù)據(jù)庫對(duì)應(yīng)多種視圖的架構(gòu)。

維基百科中的 MVC

除了上述框架中的 MVC 架構(gòu)模式,還有一些其它的書籍或者資料對(duì)于 MVC 也有著不同的解釋,比如維基百科的 Model-view-controller 條目,該條目是我們?cè)?Google 搜索 MVC 時(shí)能夠出現(xiàn)的前幾個(gè)條目,這也是維基百科中的架構(gòu)圖能夠出現(xiàn)在這篇文章中的原因 —— 有著廣泛的受眾。

維基百科中對(duì)于 MVC 架構(gòu)模式交互圖的描述其實(shí)相比上面的圖片還都是比較清晰的,這主要是因?yàn)樗鼘?duì)架構(gòu)圖中的箭頭進(jìn)行了詳細(xì)的說明,指出了這個(gè)關(guān)系到底表示什么。

  1. 視圖被用戶看到;
  2. 用戶使用控制器;
  3. 控制器操作模型;
  4. 模型更新視圖;

雖然說整個(gè)架構(gòu)圖的邏輯是可以說的通的,不過相比于前面的架構(gòu)圖總是感覺有一些奇怪,而在這幅圖片中,視圖和控制器之間是毫無關(guān)系的,這與前面見到的所有 MVC 架構(gòu)模式都完全不同,作者也不清楚這幅圖來源是什么、為什么這么畫,放在這里也僅作參考。

『標(biāo)準(zhǔn)』的 MVC

到底什么才是標(biāo)準(zhǔn)的 MVC 這個(gè)問題,到現(xiàn)在作者也沒有一個(gè) 確切的 答案;不過多個(gè)框架以及書籍對(duì) MVC 的理解有一點(diǎn)是完全相同的,也就是它們都將整個(gè)應(yīng)用分成 Model、View 和 Controller 三個(gè)部分,而這些組成部分其實(shí)也有著幾乎相同的職責(zé)。

  • 視圖:管理作為位圖展示到屏幕上的圖形和文字輸出;
  • 控制器:翻譯用戶的輸入并依照用戶的輸入操作模型和視圖;
  • 模型:管理應(yīng)用的行為和數(shù)據(jù),響應(yīng)數(shù)據(jù)請(qǐng)求(經(jīng)常來自視圖)和更新狀態(tài)的指令(經(jīng)常來自控制器);

上述內(nèi)容出自 Applications Programming in Smalltalk-80: How to use Model-View-Controller (MVC) 一文。

作者所理解的真正 MVC 架構(gòu)模式其實(shí)與 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ù),而這就是 作者所理解的 標(biāo)準(zhǔn) MVC 模式下,Model、View 和 Controller 之間的協(xié)作方式。

依賴關(guān)系

雖然我們對(duì) MVC 中的各個(gè)模塊的交互不是特別了解,但是三者之間的依賴關(guān)系卻是非常明確的;在 MVC 中,模型層可以單獨(dú)工作,而視圖層和控制器層都依賴與模型層中的數(shù)據(jù)。

雖然如上圖所示,視圖和控制器之間沒有相互依賴,不過因?yàn)橐晥D和控制器之間的依賴并不常用,所以圖中將視圖和控制器之間的依賴省略了。

分離展示層

在 Martin Fowler 對(duì)于 Model-View-Controller 的描述中,MVC 最重要的概念就是分離展示層 Separated Presentation ,如何在領(lǐng)域?qū)ο螅―omain Object)和我們?cè)谄聊簧峡吹降?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 還與觀察者同步 Observer Synchronization 關(guān)系緊密。因?yàn)樵?MVC 模式中,模型可以單獨(dú)工作,同時(shí)它對(duì)使用模型中數(shù)據(jù)的視圖和控制器一無所知,為了保持模型的獨(dú)立性,我們需要一種機(jī)制,當(dāng)模型發(fā)生改變時(shí),能夠同時(shí)更新多個(gè)視圖和控制器的內(nèi)容;在這時(shí),就需要以觀察者同步的方式解決這個(gè)問題。

我們將所有需要實(shí)時(shí)更新的組件注冊(cè)成為模型的觀察者,在模型的屬性發(fā)生變化時(shí),通過觀察者模式推送給所有注冊(cè)的觀察者(視圖和控制器)。

當(dāng)多個(gè)視圖共享相同的數(shù)據(jù)時(shí),觀察者同步是一個(gè)非常關(guān)鍵的模式,它能夠在對(duì)這些視圖不知情的前提下,同時(shí)通知多個(gè)視圖;通過觀察者模式,我們可以非常容易地創(chuàng)建一個(gè)依賴于同一模型的視圖。

觀察者同步或者說觀察者模式的主要缺點(diǎn)就是:由于事件觸發(fā)的隱式行為可能導(dǎo)致很難查找問題的來源并影響其解決,不過雖然它有著這樣的缺點(diǎn),但是觀察者同步這一機(jī)制仍然成為 MVC 以及其衍生架構(gòu)模式中非常重要的一部分。

占主導(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)在不同模塊之間能夠保持同步。

作為被動(dòng)的模型層,它對(duì)于視圖和控制器的存在并不知情,只是向外部提供接口并響應(yīng)視圖和控制器對(duì)于數(shù)據(jù)的請(qǐng)求和更新操作。

MVC + MVC

目前的大多數(shù)應(yīng)用程序都非常復(fù)雜并且同時(shí)包含客戶端和服務(wù)端,兩者分開部署但同時(shí)又都遵循 MVC 或者衍生的架構(gòu)模式;過去的 Web 應(yīng)用由于并不復(fù)雜,前端和服務(wù)端往往都部署在同一臺(tái)服務(wù)器上,比如說使用 erb 模板引擎的 Rails 或者使用 jsp 的 Java 等等;這時(shí)的 Web 應(yīng)用都遵循 MVC 架構(gòu)模式:

上圖的 MVC 架構(gòu)模式的通信方式與標(biāo)準(zhǔn)的 MVC 中不同,上圖以 Rails 為例展示其中的 MVC 是如何工作的,其中的 HTML、CSS 和 Javascript 代碼就是視圖層,控制器負(fù)責(zé)視圖的渲染并且操作模型,模型中包含部分業(yè)務(wù)邏輯并負(fù)責(zé)管理數(shù)據(jù)庫。

過去的 Web 應(yīng)用的非常簡(jiǎn)單,而現(xiàn)在的應(yīng)用程序都非常復(fù)雜,而整個(gè)應(yīng)用程序無論是 Web 還是客戶端其實(shí)都包含兩個(gè)部分,也就是前端/客戶端和后端;先拋開后端不談,無論是 Web 前端、iOS 還是 Android 都遵循 MVC 架構(gòu)模式或者它的變種。

在實(shí)際情況下,單獨(dú)的 iOS、Android 和 Web 應(yīng)用往往不能單獨(dú)工作,這些客戶端應(yīng)用需要與服務(wù)端一起工作;當(dāng)前端/客戶端與后端一同工作時(shí),其實(shí)分別『部署』了兩個(gè)不同的應(yīng)用,這兩個(gè)應(yīng)用都遵循 MVC 架構(gòu)模式:

客戶端和服務(wù)器通過網(wǎng)絡(luò)進(jìn)行連接,并組成了一個(gè)更大的 MVC 架構(gòu);從這個(gè)角度來看,服務(wù)端的模型層才存儲(chǔ)了真正的數(shù)據(jù),而客戶端的模型層只不過是一個(gè)存儲(chǔ)在客戶端設(shè)備中的本地緩存和臨時(shí)數(shù)據(jù)的集合;同理,服務(wù)端的視圖層也不是整個(gè)應(yīng)用的視圖層,用于為用戶展示數(shù)據(jù)的視圖層位于客戶端,也就是整個(gè)架構(gòu)的最頂部;中間的五個(gè)部分,也就是從低端的模型層到最上面的視圖共同組成了整個(gè)應(yīng)用的控制器,將模型中的數(shù)據(jù)以合理的方式傳遞給最上層的視圖層用于展示。

MVP

MVP 架構(gòu)模式是 MVC 的一個(gè)變種,很多框架都自稱遵循 MVC 架構(gòu)模式,但是它們實(shí)際上卻實(shí)現(xiàn)了 MVP 模式;MVC 與 MVP 之間的區(qū)別其實(shí)并不明顯,作者認(rèn)為兩者之間最大的區(qū)別就是 MVP 中使用 Presenter 對(duì)視圖和模型進(jìn)行了解耦,它們彼此都對(duì)對(duì)方一無所知,溝通都通過 Presenter 進(jìn)行。

MVP 作為一個(gè)比較有爭(zhēng)議的架構(gòu)模式,在維基百科的 Model-view-presenter 詞條中被描述為 MVC 設(shè)計(jì)模式的變種(derivation),自上個(gè)世紀(jì) 90 年代出現(xiàn)在 IBM 之后,隨著不斷的演化,雖然有著很多分支,不過 Martin Fowler 對(duì) MVP 架構(gòu)模式的定義最終被廣泛接受和討論。

在 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)視圖

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)容,本身不具有任何的邏輯:

<article class="post">
  <header class="post-header">
    <h2 class="post-title"><a href="/mvx-controller.html">談?wù)?MVX 中的 Controller</a></h2>
  </header>
  <section class="post-excerpt">
    <p>在前兩篇文章中,我們已經(jīng)對(duì) iOS 中的 Model 層以及 View 層進(jìn)行了分析,劃分出了它們的具體職責(zé),其中 Model 層除了負(fù)責(zé)數(shù)據(jù)的持久存儲(chǔ)、緩存工作,還要負(fù)責(zé)所有 HTTP... <a class="read-more" href="/mvx-controller.html">?</a></p>
  </section>
  <footer class="post-meta">
    <img class="author-thumb" src="/assets/images/draven.png" alt="Author image" nopin="nopin" />
    <a href='/author/draveness'>Draveness</a>
    <time class="post-date" datetime="2017-06-23">23 Jun 2017</time>
  </footer>
</article>

依賴關(guān)系

視圖成為了完全被動(dòng)的并且不再根據(jù)模型來更新視圖本身的內(nèi)容,也就是說,不同于 MVC 中的依賴關(guān)系;在被動(dòng)視圖中,視圖層對(duì)于模型層沒有任何的依賴:

因?yàn)橐晥D層不依賴與其他任何層級(jí)也就最大化了視圖層的可測(cè)試性,同時(shí)也將視圖層和模型層進(jìn)行了合理的分離,兩者不再相互依賴。

通信方式

被動(dòng)視圖的示意圖中一共有四條線,用于表示 Model、View 和 Presenter 之間的通信:

  1. 當(dāng)視圖接收到來自用戶的事件時(shí),會(huì)將事件轉(zhuǎn)交給 Presenter 進(jìn)行處理;
  2. 被動(dòng)的視圖向外界暴露接口,當(dāng)需要更新視圖時(shí) Presenter 通過視圖暴露的接口更新視圖的內(nèi)容;
  3. Presenter 負(fù)責(zé)對(duì)模型進(jìn)行操作和更新,在需要時(shí)取出其中存儲(chǔ)的信息;
  4. 當(dāng)模型層改變時(shí),可以將改變的信息發(fā)送給 觀察者 Presenter;

在 MVP 的變種被動(dòng)視圖中,模型的操作以及視圖的更新都僅通過 Presenter 作為中間人進(jìn)行。

監(jiān)督控制器

與被動(dòng)視圖中狀態(tài)同步都需要 顯式 的操作不同,監(jiān)督控制器(Supervising Controller)就將部分需要顯式同步的操作變成了隱式的:

在監(jiān)督控制器中,視圖層接管了一部分視圖邏輯,主要內(nèi)容就是同步 簡(jiǎn)單的 視圖和模型的狀態(tài);而監(jiān)督控制器就需要負(fù)責(zé)響應(yīng)用戶的輸入以及一部分更加復(fù)雜的視圖、模型狀態(tài)同步工作。

對(duì)于用戶輸入的處理,監(jiān)督控制器的做法與標(biāo)準(zhǔn) MVP 中的 Presenter 完全相同;但是對(duì)于視圖、模型的同步工作,監(jiān)督控制器會(huì)盡可能地將所有簡(jiǎn)單的屬性 以數(shù)據(jù)綁定的形式聲明在視圖層中 ,類似于 Vue 中雙向綁定的簡(jiǎn)化版本:

<a v-bind:href="url"></a>

剩下的無法通過上述方式直接綁定的屬性就需要通過監(jiān)督控制器來操作和更新了。

通信方式

監(jiān)督控制器中的視圖和模型層之間增加了兩者之間的耦合,也就增加了整個(gè)架構(gòu)的復(fù)雜性:

視圖和監(jiān)督控制器、模型與監(jiān)督控制器的關(guān)系與被動(dòng)視圖中兩者與 Presenter 的關(guān)系幾乎相同,視圖和模型之間新增的依賴就是數(shù)據(jù)綁定的產(chǎn)物;視圖通過聲明式的語法與模型中的簡(jiǎn)單屬性進(jìn)行綁定,當(dāng)模型發(fā)生改變時(shí),會(huì)通知其觀察者視圖作出相應(yīng)的更新。

通過這種方式能夠減輕監(jiān)督控制器的負(fù)擔(dān),減少其中簡(jiǎn)單的代碼,將一部分邏輯交由視圖進(jìn)行處理;這樣也就導(dǎo)致了視圖同時(shí)可以被 Presenter 和數(shù)據(jù)綁定兩種方式更新,相比于被動(dòng)視圖,監(jiān)督控制器的方式也降低了視圖的可測(cè)試性和封裝性。

占主導(dǎo)地位的視圖

無論是在被動(dòng)視圖還是監(jiān)督控制器版本的 MVP 架構(gòu)模式中,視圖層在整個(gè)架構(gòu)中都是占主導(dǎo)地位的:

在 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ì)方的存在完全不知情,這也是區(qū)分 MVP 和 MVC 的一個(gè)比較容易的方法。

上述內(nèi)容取自 What are MVP and MVC and what is the difference? · Stack Overflow 中的 Model-View-Controller 部分。

MVVM

相較于 MVC 和 MVP 模式,MVVM 在定義上就明確得多,同時(shí),維基百科上對(duì)于 Model-View-ViewModel 的詞條也沒有歧義;不過,在談 MVVM 架構(gòu)模式之前,我們需要先了解它是如何發(fā)展和演變的。

MVVM 的演變

早在 2004 年,Martin Fowler 發(fā)表了一篇名為 Presentation Model (以下簡(jiǎn)稱為 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 框架來簡(jiǎn)化用戶界面的創(chuàng)建的模式;我們可以認(rèn)為 MVVM 是在 WPF 平臺(tái)上對(duì)于 PM 模式的實(shí)現(xiàn) 。

有興趣的讀者可以閱讀 Introduction to Model/View/ViewModel pattern for building WPF apps 獲得更多與 MVVM 演化的相關(guān)信息。

展示模型

本節(jié)大部分內(nèi)容都節(jié)選自 Martin Fowler 的 Presentation Model 一文。

既然 MVVM 是展示模型 Presentation Model 的一個(gè)實(shí)現(xiàn),那么在介紹 Model-View-ViewModel 之前,我們就需要了解 PM 模式到底是什么。

在 MVC 一節(jié)中曾經(jīng)有過對(duì)展示層和領(lǐng)域?qū)舆M(jìn)行分離的討論,而 PM 模式就與分離展示層 Separated Presentation 有一定的關(guān)系。

作為 Martin Fowler 在 2004 年提出的概念,Presentation Model 到今天其實(shí)也是非常先進(jìn)的,PM 模式將視圖中的全部狀態(tài)和行為放到一個(gè)單獨(dú)的展示模型中,協(xié)調(diào)領(lǐng)域?qū)ο螅P停┎⑶覟橐晥D層提供一個(gè)接口。

在監(jiān)督控制器中,視圖層與模型層中的一些簡(jiǎn)單屬性進(jìn)行綁定,在模型屬性變化時(shí)直接更新視圖,而 PM 通過引入展示模型將 模型層中的數(shù)據(jù)與復(fù)雜的業(yè)務(wù)邏輯封裝成屬性與簡(jiǎn)單的數(shù)據(jù)同時(shí)暴露給視圖,讓視圖和展示模型中的屬性進(jìn)行同步 。

展示模型中包含所有的視圖渲染需要的動(dòng)態(tài)信息,包括視圖的內(nèi)容(text、color)、組件是否啟用(enable),除此之外還會(huì)將一些方法暴露給視圖用于某些事件的響應(yīng)。

狀態(tài)的同步

展示模型對(duì)于模型層的操作以及為視圖層暴露接口都是非常容易的,在整個(gè) PM 模式中,最為麻煩的就是視圖和展示模型狀態(tài)的同步。

因?yàn)檎故灸P褪且晥D的抽象,其中包含了視圖的狀態(tài)(屬性)和行為(動(dòng)作),視圖的行為可能很少發(fā)生改變,但是視圖狀態(tài)的改變就是非常常見的了,那么同步視圖和展示模型的代碼應(yīng)該放哪里就是一個(gè)需要考慮的問題了。

到目前為止,我們能夠防止?fàn)顟B(tài)同步代碼的地方其實(shí)只有兩個(gè),也就是視圖和展示模型;如果將同步的代碼放在視圖中,那么可能會(huì)影響視圖的測(cè)試,不過由于現(xiàn)在的大部分客戶端程序完全沒有測(cè)試,這一點(diǎn)其實(shí)也影響不大;如果將代碼放在展示模型中,實(shí)際上就為展示模型增加了視圖的依賴,導(dǎo)致不同層級(jí)之間的耦合。

在作者看來這兩種選擇其實(shí)都影響并不大,反正我們的應(yīng)用中并沒有測(cè)試嘛。

展示模型與其他模塊的關(guān)系

在 PM 模式中,同一個(gè)展示模型可以與多個(gè)領(lǐng)域?qū)ο蠼换,多個(gè)視圖可以使用相同的展示模型,但是每一個(gè)視圖只能持有一個(gè)展示模型。

PM 模式中不同層級(jí)之間的關(guān)系還是非常容易理解的,在這里就不做具體解釋了。

MVVM 與 WPF

MVVM 架構(gòu)模式是微軟在 2005 年誕生的,從誕生一開始就與 WPF 框架的聯(lián)系非常緊密,在這一節(jié)中,我們將介紹 MVVM 模式是如何遵循 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 模式中就是通過它完成的。

在實(shí)現(xiàn) PM 模式時(shí),我們需要處理視圖和展示模型之間狀態(tài)的同步,也就是 MVVM 中的視圖和視圖模型,我們使用隱式的 Binder 和 XAML 文件來完成視圖和視圖模型兩者之間的雙向綁定:

<Window x:Class ="WPFDataBinding.MainWindow" Title="MainWindow" Height="350" Width="604">
   <Grid>
      <Label Name="nameLabel" Margin="2">_Name:</Label>
      <TextBox Name="nameText" Grid.Column="1" Margin="2"
         Text="{Binding Name}"/>
      <Label Name="ageLabel" Margin="2" Grid.Row ="1">_Age:</Label>
      <TextBox Name="ageText" Grid.Column="1" Grid.Row ="1" Margin ="2"
         Text="{Binding Age}"/>
   </Grid>
</Window>

在 WPF 中我們可以使用 Binding 關(guān)鍵字在 XAML 中完成雙向綁定,當(dāng) TextBox 中的文字更新時(shí),Binder 也會(huì)更新 ViewModel 中對(duì)應(yīng)屬性 Name 或者 Age 的值。

我們可以說 MVVM 將視圖和展示模型之間的同步代碼放到了視圖層(XAML)中,也可以說通過隱式的方法實(shí)現(xiàn)了狀態(tài)的同步。

無論是 MVVM 還是 Presentation Model,其中最重要的不是如何同步視圖和展示模型/視圖模型之間的狀態(tài),是使用觀察者模式、雙向綁定還是其它的機(jī)制都不是整個(gè)模式中最重要的部分,最為關(guān)鍵的是 展示模型/視圖模型創(chuàng)建了一個(gè)視圖的抽象,將視圖中的狀態(tài)和行為抽離出一個(gè)新的抽象 ,這才是 MVVM 和 PM 中需要注意的。

總結(jié)

從 MVC 架構(gòu)模式到 MVVM,從分離展示層到展示模型層,經(jīng)過幾十年的發(fā)展和演變,MVC 架構(gòu)模式出現(xiàn)了各種各樣的變種,并在不同的平臺(tái)上有著自己的實(shí)現(xiàn)。

在架構(gòu)模式的選用時(shí),我們往往沒有太多的發(fā)言權(quán),主要因?yàn)槠脚_(tái)本身往往對(duì)應(yīng)用層有著自己的設(shè)計(jì),我們?cè)陂_發(fā)客戶端或者前端應(yīng)用時(shí),只需要遵循平臺(tái)固有的設(shè)計(jì)就可以完成應(yīng)用的開發(fā);不過,在有些時(shí)候,由于工程變得龐大、業(yè)務(wù)邏輯變得異常復(fù)雜,我們也可以考慮在原有的架構(gòu)之上實(shí)現(xiàn)一個(gè)新的架構(gòu)以滿足工程上的需要。

各種架構(gòu)模式的作用就是分離關(guān)注,將屬于不同模塊的功能分散到合適的位置中,同時(shí)盡量降低各個(gè)模塊的相互依賴并且減少需要聯(lián)系的膠水代碼。文中對(duì)于 MVC、MVP 和 MVVM 架構(gòu)模式的描述很難不摻雜作者的主觀意見,如果對(duì)文章中的內(nèi)容有疑問,歡迎提出不同的意見進(jìn)行討論。

Reference

  • MVC Index
  • The Model-View-Controller (MVC) Its Past and Present
  • The evolution of the Dolphin Smalltalk MVP application framework
  • MVP: Model-View-Presenter · The Taligent Programming Model for C++ and Java
  • Implementing the Model-View-ViewModel Pattern · MSDN
  • GUI Architectures · Martin Fowler
  • GUI 應(yīng)用程序架構(gòu)的十年變遷
  • Elm Architecture Tutorial · GitHub
  • Presentation Model · Martin Fowler
  • Model-view-controller · Wikipedia
  • Model-view-presenter · Wikipedia
  • Model-view-viewmodel · Wikipedia
  • Thing-Model-View-Editor
  • ASP.NET MVC Overview · MSDN
  • Intermediate Rails: Understanding Models, Views and Controllers
  • Passive View · Martin Fowler
  • Supervising Controller · Martin Fowler
  • Applications Programming in Smalltalk-80: How to use Model-View-Controller (MVC)
  • What are MVP and MVC and what is the difference? · Stack Overflow
  • Model-View-Presenter Pattern
  • Patterns - WPF Apps With The Model-View-ViewModel Design Pattern · MSDN
  • Introduction to Model/View/ViewModel pattern for building WPF apps
  • 設(shè)計(jì)模式

 

來自:http://draveness.me/mvx.html

 

標(biāo)簽: Google isp 代碼 服務(wù)器 開發(fā)者 數(shù)據(jù)庫 搜索 通信 網(wǎng)絡(luò)

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:資深程序員:給Python軟件開發(fā)測(cè)試的25個(gè)忠告!

下一篇:Python裝飾器為什么難理解?