淺談 MVC、MVP 和 MVVM 架構模式
這是 MVX 系列的第四篇文章,在前面的文章中,我們先后介紹了 iOS 中的 Model、View 和 Controller 的現狀,對比了其他平臺中的設計,最后給出了作者理想中的結構。
而在這一篇文章中,作者會依次介紹 MVC、MVP 以及 MVVM 架構模式以及不同平臺對它們的使用;雖然參考了諸多資料,不過文中觀點難免摻入作者的主觀意見,作者也希望文中的錯誤與不足之處能被各位讀者指出。
前面的幾篇文章中重點都是介紹 iOS 平臺上的 Model、View 和 Controller 如何設計,而這篇文章會對目前 GUI 應用中的 MVC、MVP 和 MVVM 架構模式進行詳細地介紹。
MVC
在整個 GUI 編程領域,MVC 已經擁有將近 50 年的歷史了。早在幾十年前,Smalltalk-76 就對 MVC 架構模式進行了實現,在隨后的幾十年歷史中,MVC 產生了很多的變種,例如:HMVC、MVA、MVP、MVVM 和其它將 MVC 運用于其它不同領域的模式。
早期的 MVC
而本文的內容就是從 MVC 開始的,作為架構模式中最出名并且應用最廣泛的架構模式,MVC 并沒有一個 明確的 定義,網上流傳的 MVC 架構圖也是形態各異,作者查閱了很多資料也沒有辦法確定到底什么樣的架構圖才是 標準的 MVC 實現。
設計 MVC 的重要目的就是在人的心智模型與計算機的模型之間建立一個橋梁,而 MVC 能夠解決這一問題并 為用戶提供直接看到信息和操作信息的功能 。
更早的概念類似 Model-View-Editor(MVE)這里就不再提及了,感興趣的讀者可以閱讀這篇論文 Thing-Model-View-Editor 了解更多的信息。
混亂的 MVC 架構
作者相信,稍有編程經驗的開發者就對 MVC 有所了解,至少也是聽過 MVC 的名字。作者也一直都認為絕大多數人對于 MVC 理解的概念都一樣,很多人對于 MVVM 的實現有很大爭論,說遵循什么什么架構的是 MVVM,MVVM 有什么組件、沒有什么組件,而對于 MVC 仿佛沒有那么大的疑問,這其實卻不然。
ASP.NET MVC
在最近的幾個月,作者發現不同人對于 MVC 的理解有巨大的差異,這是 ASP.NET MVC Overview 一文中對于 MVC 模式描述的示意圖。
圖片中并沒有對 Model、View 和 Controller 三者之間如何交互進行說明,有的也只是幾個箭頭。我們應該可以這么簡單地理解:
- 控制器負責管理視圖和模型;
- 視圖負責展示模型中的內容;
由于文章沒有明確對這三個箭頭的含義進行解釋,所以在這里也僅作推斷,無法確認原作者的意思。
Spring MVC
與 ASP.NET 不同,Spring MVC 對于 MVC 架構模式的實現就更加復雜了,增加了一個用于分發請求、管理視圖的 DispatchServlet:
在這里不再介紹 Spring MVC 對于 HTTP 請求的處理流程,我們對其中 Model、View 和 Controller 之間的關系進行簡單的分析:
- 通過 DispatchServlet 將控制器層和視圖層完全解耦;
- 視圖層和模型層之間沒有直接關系,只有間接關系,通過控制器對模型進行查詢、返回給 DispatchServlet 后再傳遞至視圖層;
雖然 Spring MVC 也聲稱自己遵循 MVC 架構模式,但是這里的 MVC 架構模式和 ASP.NET 中卻有很大的不同。
iOS MVC
iOS 客戶端中的 Cocoa Touch 自古以來就遵循 MVC 架構模式,不過 Cocoa Touch 中的 MVC 與 ASP.NET 和 Spring 中的 MVC 截然不同。
在 iOS 中,由于 UIViewController 類持有一個根視圖 UIView ,所以視圖層與控制器層是緊密耦合在一起的,這也是 iOS 項目經常遇到視圖控制器非常臃腫的重要原因之一。
Rails MVC
Rails 作為著名的 MVC 框架,視圖層和模型層沒有直接的耦合,而是通過控制器作為中間人對信息進行傳遞:
這種 MVC 的設計分離了視圖層和模型層之間的耦合,作為承擔數據存儲功能的模型層,可以通過控制器同時為多種不同的視圖提供數據:
控制器根據用戶發出的 HTTP 請求,從模型中取出相同的數據,然后傳給不同的視圖以渲染出不同的結果。Rails 中的 MVC 架構模式能夠很好地將用于展示的視圖和用于存儲數據的數據庫進行分離,兩者之間通過控制器解耦,能夠實現同一數據庫對應多種視圖的架構。
維基百科中的 MVC
除了上述框架中的 MVC 架構模式,還有一些其它的書籍或者資料對于 MVC 也有著不同的解釋,比如維基百科的 Model-view-controller 條目,該條目是我們在 Google 搜索 MVC 時能夠出現的前幾個條目,這也是維基百科中的架構圖能夠出現在這篇文章中的原因 —— 有著廣泛的受眾。
維基百科中對于 MVC 架構模式交互圖的描述其實相比上面的圖片還都是比較清晰的,這主要是因為它對架構圖中的箭頭進行了詳細的說明,指出了這個關系到底表示什么。
- 視圖被用戶看到;
- 用戶使用控制器;
- 控制器操作模型;
- 模型更新視圖;
雖然說整個架構圖的邏輯是可以說的通的,不過相比于前面的架構圖總是感覺有一些奇怪,而在這幅圖片中,視圖和控制器之間是毫無關系的,這與前面見到的所有 MVC 架構模式都完全不同,作者也不清楚這幅圖來源是什么、為什么這么畫,放在這里也僅作參考。
『標準』的 MVC
到底什么才是標準的 MVC 這個問題,到現在作者也沒有一個 確切的 答案;不過多個框架以及書籍對 MVC 的理解有一點是完全相同的,也就是它們都將整個應用分成 Model、View 和 Controller 三個部分,而這些組成部分其實也有著幾乎相同的職責。
- 視圖:管理作為位圖展示到屏幕上的圖形和文字輸出;
- 控制器:翻譯用戶的輸入并依照用戶的輸入操作模型和視圖;
- 模型:管理應用的行為和數據,響應數據請求(經常來自視圖)和更新狀態的指令(經常來自控制器);
上述內容出自 Applications Programming in Smalltalk-80: How to use Model-View-Controller (MVC) 一文。
作者所理解的真正 MVC 架構模式其實與 ASP.NET 中對于 MVC 的設計完全相同:
控制器負責對模型中的數據進行更新,而視圖向模型中請求數據;當有用戶的行為觸發操作時,會有控制器更新模型,并通知視圖進行更新,在這時視圖向模型請求新的數據,而這就是 作者所理解的 標準 MVC 模式下,Model、View 和 Controller 之間的協作方式。
依賴關系
雖然我們對 MVC 中的各個模塊的交互不是特別了解,但是三者之間的依賴關系卻是非常明確的;在 MVC 中,模型層可以單獨工作,而視圖層和控制器層都依賴與模型層中的數據。
雖然如上圖所示,視圖和控制器之間沒有相互依賴,不過因為視圖和控制器之間的依賴并不常用,所以圖中將視圖和控制器之間的依賴省略了。
分離展示層
在 Martin Fowler 對于 Model-View-Controller 的描述中,MVC 最重要的概念就是分離展示層 Separated Presentation ,如何在領域對象(Domain Object)和我們在屏幕上看到的 GUI 元素進行劃分是 MVC 架構模式中最核心的問題。
GUI 應用程序由于其需要展示內容的特點,分為兩個部分:一部分是用于展示內容的展示層(Presentation Layer),另一部分包含領域和數據邏輯的領域層(Domain Layer)。
展示層依賴于領域層中存儲的數據,而領域層對于展示層一無所知,領域層其實也是 MVC 模式中的模型層,而展示層可以理解為 VC 部分。
MVC 最重要的目的并不是規定各個模塊應該如何交互和聯系,而是將原有的混亂的應用程序劃分出合理的層級,把一團混亂的代碼,按照展示層和領域層分成兩個部分;在這時,領域層中的領域對象由于其自身特點不需要對展示層有任何了解,可以同時為不同的展示層工作。
觀察者同步
除了分離展示層,MVC 還與觀察者同步 Observer Synchronization 關系緊密。因為在 MVC 模式中,模型可以單獨工作,同時它對使用模型中數據的視圖和控制器一無所知,為了保持模型的獨立性,我們需要一種機制,當模型發生改變時,能夠同時更新多個視圖和控制器的內容;在這時,就需要以觀察者同步的方式解決這個問題。
我們將所有需要實時更新的組件注冊成為模型的觀察者,在模型的屬性發生變化時,通過觀察者模式推送給所有注冊的觀察者(視圖和控制器)。
當多個視圖共享相同的數據時,觀察者同步是一個非常關鍵的模式,它能夠在對這些視圖不知情的前提下,同時通知多個視圖;通過觀察者模式,我們可以非常容易地創建一個依賴于同一模型的視圖。
觀察者同步或者說觀察者模式的主要缺點就是:由于事件觸發的隱式行為可能導致很難查找問題的來源并影響其解決,不過雖然它有著這樣的缺點,但是觀察者同步這一機制仍然成為 MVC 以及其衍生架構模式中非常重要的一部分。
占主導地位的控制器
MVC 架構模式的三個組成部分:Model、View 和 Controller 中最重要的就是控制器,它承擔了整個架構中的大部分業務邏輯,同時在用戶請求到達或者事件發生時都會首先通知控制器并由它來決定如何響應這次請求或者事件。
在 MVC 中,所有的用戶請求都會首先交給控制器,再由控制器來決定如何響應用戶的輸入,無論是更新模型中的信息還是渲染相應的視圖,都是通過控制器來決定的;也就是說,在 MVC 中,控制器占據主導地位,它決定用戶的輸入是如何被處理的。
被動的模型
在絕大多數的 MVC 架構模式中,模型都不會主動向視圖或者控制器推送消息;模型都是被動的,它只存儲整個應用中的數據,而信息的獲取和更新都是由控制器來驅動的。
但是當模型中的數據發生變化時,卻需要通過一些方式通知對應的視圖進行更新,在這種情況下其實也不需要模型 主動 將數據變化的消息推送給視圖;因為所有對于模型層的改變都是 由用戶的操作導致的 ,而用戶的操作都是通過控制器來處理的,所以只需要在控制器改變模型時,將更新的信息發送給視圖就可以了;當然,我們也可以通過 觀察者模式 向未知的觀察者發送通知,以保證狀態在不同模塊之間能夠保持同步。
作為被動的模型層,它對于視圖和控制器的存在并不知情,只是向外部提供接口并響應視圖和控制器對于數據的請求和更新操作。
MVC + MVC
目前的大多數應用程序都非常復雜并且同時包含客戶端和服務端,兩者分開部署但同時又都遵循 MVC 或者衍生的架構模式;過去的 Web 應用由于并不復雜,前端和服務端往往都部署在同一臺服務器上,比如說使用 erb 模板引擎的 Rails 或者使用 jsp 的 Java 等等;這時的 Web 應用都遵循 MVC 架構模式:
上圖的 MVC 架構模式的通信方式與標準的 MVC 中不同,上圖以 Rails 為例展示其中的 MVC 是如何工作的,其中的 HTML、CSS 和 Javascript 代碼就是視圖層,控制器負責視圖的渲染并且操作模型,模型中包含部分業務邏輯并負責管理數據庫。
過去的 Web 應用的非常簡單,而現在的應用程序都非常復雜,而整個應用程序無論是 Web 還是客戶端其實都包含兩個部分,也就是前端/客戶端和后端;先拋開后端不談,無論是 Web 前端、iOS 還是 Android 都遵循 MVC 架構模式或者它的變種。
在實際情況下,單獨的 iOS、Android 和 Web 應用往往不能單獨工作,這些客戶端應用需要與服務端一起工作;當前端/客戶端與后端一同工作時,其實分別『部署』了兩個不同的應用,這兩個應用都遵循 MVC 架構模式:
客戶端和服務器通過網絡進行連接,并組成了一個更大的 MVC 架構;從這個角度來看,服務端的模型層才存儲了真正的數據,而客戶端的模型層只不過是一個存儲在客戶端設備中的本地緩存和臨時數據的集合;同理,服務端的視圖層也不是整個應用的視圖層,用于為用戶展示數據的視圖層位于客戶端,也就是整個架構的最頂部;中間的五個部分,也就是從低端的模型層到最上面的視圖共同組成了整個應用的控制器,將模型中的數據以合理的方式傳遞給最上層的視圖層用于展示。
MVP
MVP 架構模式是 MVC 的一個變種,很多框架都自稱遵循 MVC 架構模式,但是它們實際上卻實現了 MVP 模式;MVC 與 MVP 之間的區別其實并不明顯,作者認為兩者之間最大的區別就是 MVP 中使用 Presenter 對視圖和模型進行了解耦,它們彼此都對對方一無所知,溝通都通過 Presenter 進行。
MVP 作為一個比較有爭議的架構模式,在維基百科的 Model-view-presenter 詞條中被描述為 MVC 設計模式的變種(derivation),自上個世紀 90 年代出現在 IBM 之后,隨著不斷的演化,雖然有著很多分支,不過 Martin Fowler 對 MVP 架構模式的定義最終被廣泛接受和討論。
在 MVP 中,Presenter 可以理解為松散的控制器,其中包含了視圖的 UI 業務邏輯,所有從視圖發出的事件,都會通過代理給 Presenter 進行處理;同時,Presenter 也通過視圖暴露的接口與其進行通信。
目前常見的 MVP 架構模式其實都是它的變種: Passive View 和 Supervising Controller ,接下來的內容也是圍繞這兩種變種進行展開的。
被動視圖
MVP 的第一個主要變種就是被動視圖(Passive View);顧名思義,在該變種的架構模式中,視圖層是被動的,它本身不會改變自己的任何的狀態,所有的狀態都是通過 Presenter 來間接改變的。
被動的視圖層就像前端中的 HTML 和 CSS 代碼,只負責展示視圖的結構和內容,本身不具有任何的邏輯:
<article class="post"> <header class="post-header"> <h2 class="post-title"><a href="/mvx-controller.html">談談 MVX 中的 Controller</a></h2> </header> <section class="post-excerpt"> <p>在前兩篇文章中,我們已經對 iOS 中的 Model 層以及 View 層進行了分析,劃分出了它們的具體職責,其中 Model 層除了負責數據的持久存儲、緩存工作,還要負責所有 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>
依賴關系
視圖成為了完全被動的并且不再根據模型來更新視圖本身的內容,也就是說,不同于 MVC 中的依賴關系;在被動視圖中,視圖層對于模型層沒有任何的依賴:
因為視圖層不依賴與其他任何層級也就最大化了視圖層的可測試性,同時也將視圖層和模型層進行了合理的分離,兩者不再相互依賴。
通信方式
被動視圖的示意圖中一共有四條線,用于表示 Model、View 和 Presenter 之間的通信:
- 當視圖接收到來自用戶的事件時,會將事件轉交給 Presenter 進行處理;
- 被動的視圖向外界暴露接口,當需要更新視圖時 Presenter 通過視圖暴露的接口更新視圖的內容;
- Presenter 負責對模型進行操作和更新,在需要時取出其中存儲的信息;
- 當模型層改變時,可以將改變的信息發送給 觀察者 Presenter;
在 MVP 的變種被動視圖中,模型的操作以及視圖的更新都僅通過 Presenter 作為中間人進行。
監督控制器
與被動視圖中狀態同步都需要 顯式 的操作不同,監督控制器(Supervising Controller)就將部分需要顯式同步的操作變成了隱式的:
在監督控制器中,視圖層接管了一部分視圖邏輯,主要內容就是同步 簡單的 視圖和模型的狀態;而監督控制器就需要負責響應用戶的輸入以及一部分更加復雜的視圖、模型狀態同步工作。
對于用戶輸入的處理,監督控制器的做法與標準 MVP 中的 Presenter 完全相同;但是對于視圖、模型的同步工作,監督控制器會盡可能地將所有簡單的屬性 以數據綁定的形式聲明在視圖層中 ,類似于 Vue 中雙向綁定的簡化版本:
<a v-bind:href="url"></a>
剩下的無法通過上述方式直接綁定的屬性就需要通過監督控制器來操作和更新了。
通信方式
監督控制器中的視圖和模型層之間增加了兩者之間的耦合,也就增加了整個架構的復雜性:
視圖和監督控制器、模型與監督控制器的關系與被動視圖中兩者與 Presenter 的關系幾乎相同,視圖和模型之間新增的依賴就是數據綁定的產物;視圖通過聲明式的語法與模型中的簡單屬性進行綁定,當模型發生改變時,會通知其觀察者視圖作出相應的更新。
通過這種方式能夠減輕監督控制器的負擔,減少其中簡單的代碼,將一部分邏輯交由視圖進行處理;這樣也就導致了視圖同時可以被 Presenter 和數據綁定兩種方式更新,相比于被動視圖,監督控制器的方式也降低了視圖的可測試性和封裝性。
占主導地位的視圖
無論是在被動視圖還是監督控制器版本的 MVP 架構模式中,視圖層在整個架構中都是占主導地位的:
在 MVC 中,控制器負責 以不同的視圖響應客戶端請求的不同動作 ;然而,不同于 MVC 模式,MVP 中視圖將所有的動作交給 Presenter 進行處理;MVC 中的所有的動作都對應著一個控制器的方法調用,Web 應用中的每一個動作都是對某一個 URL 進行的操作,控制器根據訪問的路由和方法(GET 等)對數據進行操作,最終選擇正確的視圖進行返回。
MVC 中控制器返回的視圖沒有直接綁定到模型上,它僅僅被控制器渲染并且是完全無狀態的,其中不包含任何的邏輯,但是 MVP 中的視圖 必須要將對應的事件代理給 Presenter 執行 ,否則事件就無法被響應。
另一個 MVP 與 MVC 之間的重大區別就是,MVP(Passive View)中的視圖和模型是完全解耦的,它們對于對方的存在完全不知情,這也是區分 MVP 和 MVC 的一個比較容易的方法。
上述內容取自 What are MVP and MVC and what is the difference? · Stack Overflow 中的 Model-View-Controller 部分。
MVVM
相較于 MVC 和 MVP 模式,MVVM 在定義上就明確得多,同時,維基百科上對于 Model-View-ViewModel 的詞條也沒有歧義;不過,在談 MVVM 架構模式之前,我們需要先了解它是如何發展和演變的。
MVVM 的演變
早在 2004 年,Martin Fowler 發表了一篇名為 Presentation Model (以下簡稱為 PM 模式)的文章,PM 模式與 MVP 比較相似,它從視圖層中分離了行為和狀態;PM 模式中創建了一個視圖的抽象,叫做 Presentation Model,而視圖也成為了這個模型的『渲染』結果。
2005 年,John Gossman 在他的博客上公布了 Introduction to Model/View/ViewModel pattern for building WPF apps 一文。MVVM 與 Martin Fowler 所說的 PM 模式其實是完全相同的,Fowler 提出的 PM 模式是一種與平臺無關的創建視圖抽象的方法,而 Gossman 的 MVVM 是專門用于 WPF 框架來簡化用戶界面的創建的模式;我們可以認為 MVVM 是在 WPF 平臺上對于 PM 模式的實現 。
有興趣的讀者可以閱讀 Introduction to Model/View/ViewModel pattern for building WPF apps 獲得更多與 MVVM 演化的相關信息。
展示模型
本節大部分內容都節選自 Martin Fowler 的 Presentation Model 一文。
既然 MVVM 是展示模型 Presentation Model 的一個實現,那么在介紹 Model-View-ViewModel 之前,我們就需要了解 PM 模式到底是什么。
在 MVC 一節中曾經有過對展示層和領域層進行分離的討論,而 PM 模式就與分離展示層 Separated Presentation 有一定的關系。
作為 Martin Fowler 在 2004 年提出的概念,Presentation Model 到今天其實也是非常先進的,PM 模式將視圖中的全部狀態和行為放到一個單獨的展示模型中,協調領域對象(模型)并且為視圖層提供一個接口。
在監督控制器中,視圖層與模型層中的一些簡單屬性進行綁定,在模型屬性變化時直接更新視圖,而 PM 通過引入展示模型將 模型層中的數據與復雜的業務邏輯封裝成屬性與簡單的數據同時暴露給視圖,讓視圖和展示模型中的屬性進行同步 。
展示模型中包含所有的視圖渲染需要的動態信息,包括視圖的內容(text、color)、組件是否啟用(enable),除此之外還會將一些方法暴露給視圖用于某些事件的響應。
狀態的同步
展示模型對于模型層的操作以及為視圖層暴露接口都是非常容易的,在整個 PM 模式中,最為麻煩的就是視圖和展示模型狀態的同步。
因為展示模型是視圖的抽象,其中包含了視圖的狀態(屬性)和行為(動作),視圖的行為可能很少發生改變,但是視圖狀態的改變就是非常常見的了,那么同步視圖和展示模型的代碼應該放哪里就是一個需要考慮的問題了。
到目前為止,我們能夠防止狀態同步代碼的地方其實只有兩個,也就是視圖和展示模型;如果將同步的代碼放在視圖中,那么可能會影響視圖的測試,不過由于現在的大部分客戶端程序完全沒有測試,這一點其實也影響不大;如果將代碼放在展示模型中,實際上就為展示模型增加了視圖的依賴,導致不同層級之間的耦合。
在作者看來這兩種選擇其實都影響并不大,反正我們的應用中并沒有測試嘛。
展示模型與其他模塊的關系
在 PM 模式中,同一個展示模型可以與多個領域對象交互,多個視圖可以使用相同的展示模型,但是每一個視圖只能持有一個展示模型。
PM 模式中不同層級之間的關系還是非常容易理解的,在這里就不做具體解釋了。
MVVM 與 WPF
MVVM 架構模式是微軟在 2005 年誕生的,從誕生一開始就與 WPF 框架的聯系非常緊密,在這一節中,我們將介紹 MVVM 模式是如何遵循 PM 模式實現的,WPF 作為微軟用于處理 GUI 軟件的框架,提供了一套非常優雅的解決方案。
從 Model-View-ViewModel 這個名字來看,它由三個部分組成,也就是 Model、View 和 ViewModel;其中視圖模型(ViewModel)其實就是 PM 模式中的展示模型,在 MVVM 中叫做視圖模型。
除了我們非常熟悉的 Model、View 和 ViewModel 這三個部分,在 MVVM 的實現中,還引入了 隱式的 一個 Binder 層,而聲明式的數據和命令的綁定在 MVVM 模式中就是通過它完成的。
在實現 PM 模式時,我們需要處理視圖和展示模型之間狀態的同步,也就是 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 關鍵字在 XAML 中完成雙向綁定,當 TextBox 中的文字更新時,Binder 也會更新 ViewModel 中對應屬性 Name 或者 Age 的值。
我們可以說 MVVM 將視圖和展示模型之間的同步代碼放到了視圖層(XAML)中,也可以說通過隱式的方法實現了狀態的同步。
無論是 MVVM 還是 Presentation Model,其中最重要的不是如何同步視圖和展示模型/視圖模型之間的狀態,是使用觀察者模式、雙向綁定還是其它的機制都不是整個模式中最重要的部分,最為關鍵的是 展示模型/視圖模型創建了一個視圖的抽象,將視圖中的狀態和行為抽離出一個新的抽象 ,這才是 MVVM 和 PM 中需要注意的。
總結
從 MVC 架構模式到 MVVM,從分離展示層到展示模型層,經過幾十年的發展和演變,MVC 架構模式出現了各種各樣的變種,并在不同的平臺上有著自己的實現。
在架構模式的選用時,我們往往沒有太多的發言權,主要因為平臺本身往往對應用層有著自己的設計,我們在開發客戶端或者前端應用時,只需要遵循平臺固有的設計就可以完成應用的開發;不過,在有些時候,由于工程變得龐大、業務邏輯變得異常復雜,我們也可以考慮在原有的架構之上實現一個新的架構以滿足工程上的需要。
各種架構模式的作用就是分離關注,將屬于不同模塊的功能分散到合適的位置中,同時盡量降低各個模塊的相互依賴并且減少需要聯系的膠水代碼。文中對于 MVC、MVP 和 MVVM 架構模式的描述很難不摻雜作者的主觀意見,如果對文章中的內容有疑問,歡迎提出不同的意見進行討論。
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 應用程序架構的十年變遷
- 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
- 設計模式
來自:http://draveness.me/mvx.html