MVC模式(Model–view–controller)是軟件工程中的一種軟件架構模式,把軟件系統分為三個基本部分:模型(Model)、視圖(View)和控制器(Controller)。

MVC模式最早由Trygve Reenskaug在1978年提出[1],是施樂帕羅奧多研究中心(Xerox PARC)在20世紀80年代為程式語言Smalltalk發明的一種軟件架構。MVC模式的目的是實現一種動態的程式設計,使後續對程式的修改和擴充簡化,並且使程式某一部分的重複利用成為可能。除此之外,此模式透過對複雜度的簡化,使程式結構更加直覺。軟件系統透過對自身基本部分分離的同時也賦予了各個基本部分應有的功能。專業人員可以依據自身的專長分組:

  • 模型(Model) - 程式設計師編寫程式應有的功能(實現演算法等等)、資料庫專家進行數據管理和資料庫設計(可以實現具體的功能)。
  • 視圖(View) - 介面設計人員進行圖形介面設計。
  • 控制器(Controller)- 負責轉發請求,對請求進行處理。
MVC模式

組件的互動

Thumb
MVC組件之間的典型合作

將應用程式劃分為三種組件,模型 - 視圖 - 控制器(MVC)設計定義它們之間的相互作用。[2]

  • 模型(Model) 用於封裝與應用程式的業務邏輯相關的數據以及對數據的處理方法。「 Model 」有對數據直接訪問的權力,例如對資料庫的訪問。「Model」不依賴「View」和「Controller」,也就是說, Model 不關心它會被如何顯示或是如何被操作。但是 Model 中數據的變化一般會通過一種重新整理機制被公佈。為了實現這種機制,那些用於監視此 Model 的 View 必須事先在此 Model 上註冊,從而,View 可以了解在數據 Model 上發生的改變。(比如:觀察者模式
  • 視圖(View)能夠實現數據有目的的顯示(理論上,這不是必需的)。在 View 中一般沒有程式上的邏輯。為了實現 View 上的重新整理功能,View 需要訪問它監視的數據模型(Model),因此應該事先在被它監視的數據那裏註冊。
  • 控制器(Controller)起到不同層面間的組織作用,用於控制應用程式的流程。它處理事件並作出響應。「事件」包括用戶的行為和數據 Model 上的改變。

優點

在最初的JSP網頁中,像資料庫查詢陳述式(SQL query)這樣的數據層代碼和像HTML這樣的表示層代碼是混在一起。雖然有着經驗比較豐富的開發者會將數據從表示層分離開來,但這樣的良好設計通常並不是很容易做到的,實現它需要精心地計劃和不斷的嘗試。MVC可以從根本上強制性地將它們分開。儘管構造MVC應用程式需要一些額外的工作,但是它帶給我們的好處是毋庸置疑的。

首先,多個 View 能共用一個 Model 。如今,同一個Web應用程式會提供多種用戶介面,例如用戶希望既能夠通過瀏覽器來收發電子郵件,還希望通過手機來訪問電子郵箱,這就要求Web網站同時能提供Internet介面和WAP介面。在MVC設計模式中, Model 響應用戶請求並返迴響應數據,View 負責格式化數據並把它們呈現給用戶,業務邏輯和表示層分離,同一個 Model 可以被不同的 View 重用,所以大大提高了代碼的可重用性。

其次,Controller 是自包含(self-contained,指高獨立內聚)的物件,與 Model 和 View 保持相對獨立,所以可以方便的改變應用程式的數據層和業務規則。例如,把資料庫從MySQL移植到Oracle,或者把RDBMS數據源改變成LDAP數據源,只需改變 Controller 即可。一旦正確地實現了控制器,不管數據來自資料庫還是LDAP伺服器,View 都會正確地顯示它們。由於MVC模式的三個模組相互獨立,改變其中一個不會影響其他兩個,所以依據這種設計思想能構造良好的少互擾性的構件。

此外,Controller 提高了應用程式的靈活性和可組態性。Controller 可以用來連接不同的 Model 和 View 去完成用戶的需求,也可以構造應用程式提供強有力的手段。給定一些可重用的 Model 、 View 和Controller 可以根據用戶的需求選擇適當的 Model 進行處理,然後選擇適當的的 View 將處理結果顯示給用戶。

評價、誤解及適用範圍

MVC模式在概念上強調 Model, View, Controller 的分離,各個模組也遵循着由 Controller 來處理訊息,Model 掌管資料來源,View 負責資料顯示的職責分離原則,因此在實作上,MVC 模式的 Framework 通常會將 MVC 三個部份分離實作:

  • Model 負責資料存取,較現代的 Framework 都會建議使用獨立的資料物件 (DTO, POCO, POJO 等) 來替代弱型別的集合物件。資料存取的程式碼會使用 Data Access 的程式碼或是 ORM-based Framework,也可以進一步使用 Repository Pattern 與 Unit of Works Pattern 來切割資料來源的相依性。
  • Controller 負責處理訊息,較高階的 Framework 會有一個預設的實作來作為 Controller 的基礎,例如 Spring 的 DispatcherServlet 或是 ASP.NET MVC 的 Controller 等,在職責分離原則的基礎上,每個 Controller 負責的部份不同,因此會將各個 Controller 切割成不同的檔案以利維護。
  • View 負責顯示資料,這個部份多為前端應用,而 Controller 會有一個機制將處理的結果 (可能是 Model, 集合或是狀態等) 交給 View,然後由 View 來決定怎麼顯示。例如 Spring Framework 使用 JSP 或相應技術,ASP.NET MVC 則使用 Razor 處理資料的顯示。

也因為 MVC 模式強調職責分離,所以在發展 MVC 應用時會產生很多檔案,在 IDE (整合開發環境) 不夠成熟時它會是個問題,但在現代主流 IDE 都能使用類別物件的資訊來組織程式碼編輯的情況下,多檔案早已不是問題,而且 MVC 模式會要求開發者進一步思考應用程式的架構 (Application Architecture),而非用大雜燴的方式開發應用程式,對於應用程式的生命週期以及後續的可擴充與可維護性而言有相當正面的幫助。另外,MVC 職責分離也帶來了一個現代軟件工程要求的重要特性:可測試性 (Testability),MVC-based 的應用程式在良好的職責分離的設計下,各個部份可獨立行使單元測試,有利於與企業內的自動化測試、持續整合 (Continuous Integration) 與持續交付 (Continuous Delivery) 流程整合,減少應用程式改版部署所需的時間。

MVC 模式的應用程式的目的就是希望打破以往應用程式使用的大雜燴程式撰寫方式,並間接誘使開發人員以更高的架構導向思維來思考應用程式的設計,因此對於一個剛入門的初學者來說,架構導向的思考會有一定的門檻,需要較多的實作與練習才能具備相應的能力,大多數的初學者還是較習慣於大雜燴式的程式撰寫,所以可能會對 MVC 模式抱持着排斥或厭惡的心態,然而 MVC(或是其他的設計模式)都是有助於應用程式長遠的發展,雖然大雜燴式的程式也可以用來發展長生命週期的應用程式,但是相較於 MVC,大雜燴式的程式在可擴充性和可維護性 (尤其是可測試性) 上會遠比 MVC 複雜很多,相反的,MVC 模式的應用程式是在初始開發時期必須先思考並使用軟件架構,使得開發時期會需要花較多心力,但是一旦應用程式完成後,可擴充性、可維護性和可測試性反而會因為 MVC 的特性而變得容易。

因此,MVC 模式在已有眾多優秀 Framework 的現代,早就已經沒有不適合小型應用的問題,小型的應用還是可以由 MVC Framework 的應用來獲取 MVC 的優點,同時它也能作為未來小型應用擴充到大型應用時的基礎與入門磚。若一開始就想要做大型應用,那麼 MVC 模式的職責分離以及要求開發的架構思考會更適合大型應用的開發。

實際範例

這裏有一個通過 JavaScript 所實現的基於 MVC 模型,需要注意的是:MVC 不是一種技術,而是一種理念。

/** 模拟 Model, View, Controller */
var M = {}, V = {}, C = {};

/** Model 负责存放资料 */
M.data = "hello world";

/** View 负责将资料输出给用户 */
V.render = (M) => { alert(M.data); }

/** Controller 作为连接 M 和 V 的桥梁 */
C.handleOnload = () => { V.render(M); }

/** 在网页读取的时候呼叫 Controller */
window.onload = C.handleOnload;

在這個簡短的程式中就是一個完整的 MVC 模式。

實現

MFC

微軟所推出的MFC Document/View架構是早期對於MVC模式的實現,MFC將程式分成CView以及CDocument兩大類別,其中的Document對應MVC中的 Model ,View 相當於MVC中的 View+Controller,再加上CWinApp類別,合成三大項。但是基本上MFC是一個失敗的MVC模式作品。

由於MFC之下的Document/View 定義過於模糊,未將Controller(MessageMap)部份取出,因此 Controller 可以置入 View 或Document,但不管置入哪一方面,都會與View或Document綁死,沒有彈性。

Java

Java 平台企業版 (J2EE)

和其他的各種框架不一樣,J2EE為模型對象(Model Objects)定義了一個規範。

視圖(View)
在J2EE應用程式中,視圖(View)可能由Java Server Page(JSP)擔任。生成 View 的代碼則可能是一個servlet的一部分,特別是在客戶端伺服器端互動的時候。
控制器(Controller)
J2EE應用中,Controller 可能是一個servlet
除了可直接以J2EE來撰寫外,亦可用其他框架來撰寫,常見的有Struts2Spring Framework……等等。
模型(Model)
Model 則是由一個實體Bean來實現。

Java Swing

Swing是一個標準的MVC結構。ComponentUI代表View,負責描畫組件。組件尤其Model層,比如JTextField的Document、JTable的TableModel、JTree的TreeModel等等。Control可能不是很明顯,我們可以將Event機制看作Swing團隊為開發者設計的Controller。

作為Java開發者,如果想理解MVC的結構,學習Swing的確是個不錯的選擇。

.NET

ASP.NET

在ASP.NET中,針對視圖(View)和控制器(Controller)的模式沒有被很好地定義。而模型(Model)則留給開發者去設計。

視圖(View)
ASPX和ASCX檔案被用來處理 View 的職責。在這個設計中 View 實際上是從 Controller 繼承而來。這個和Smalltalk的實施有所不同,在Smalltalk中不同的類都有指標互相指向對方。
控制器(Controllers)
Controller 的職責被分割成兩部分。事件(Event)的產生和傳輸是框架的一部分,更明確的說是Page和Control兩個類。而事件的處理則在分離的代碼中實現。一般說來,控制器是部件,用來處理用戶互動、對model施加工作、最終選擇view來繪製。對於MVC應用程式,view只顯式資訊;控制器處理和響應用戶的輸入和互動。
模型(Model)
ASP.NET 不嚴格需要一個 Model。開發者可以自行選擇建立一個 Model 類,但是很多人選擇放棄這一步,直接把事件處理放在 Controller 里處理任何計算、數據儲存等等。但用 Model 來包含商業邏輯和數據存取是可實現的。一般說來,MVC應用程中的Model表示應用程式的狀態、應用程式能執行的任何業務邏輯或操作。強型別的views典型使用ViewModel類型來包含顯示在該view中的數據。控制器從model建立並保有這些ViewModel實例。

ASP.NET MVC

2013年10月17日ASP.NET MVC發表了穩定版本5.0。[3]

在ASP.NET MVC中,一般情況下Model通常搭配LINQ to SQL類別(使用O/R Designer工具所製作而成的DBML檔)或ADO.NET實體資料模型(Entity Data Model,使用ADO.NET Entity Framework製作出的EDMX檔)來實作。

ASP.NET Core MVC

隨着.NET Core的發佈,2016年5月17日發佈了ASP.NET Core MVC 1.0.0-rc2。現已發展為ASP.NET Core MVC 6。

Windows Forms

在WinForms中,這個針對視圖(View)和控制器(Controller)的模式已經很好的定義。而模型(Model)則留給開發者去設計。

視圖(View)
由Form或者Control類繼承來的一個類處理 View 的職責。在WinForm這個例子中 View 和 Controller 被編譯在同一個類中,這個和ASP.NET不同。
控制器(Controller)
Controller 的職責被分割成三部分。事件(Event)的產生和傳輸是作業系統的一部分。在.Net框架中Form和Control類將不同的事件轉發給相應的事件處理器。而事件的處理則在分離的代碼中實現。
模型(Model)
就像ASP.NET一樣,WinForm不嚴格需要一個 Model。開發者可以自行選擇建立一個 Model 類,但是很多人選擇放棄這一步,直接把事件處理放在 Controller 里處理任何計算、數據儲存等等。也就是說用Model來包含商業邏輯和數據存取。

Perl

CatalystJifty是透過Perl語言所開發出來的Web Framework,都採用Model-View-Controller 架構。Catalyst 本身只是做了 Controller,View 和 Model 讓開發者自由選用 CPAN 上的模組開發,例如 Template 和 Template Declare 都可用來產生視圖。Jifty 將 MVC 完全實做完成,View 的部份在早期版本使用 Mason 實做,較新版本使用 Template Declare。

Ruby on Rails

Ruby on Rails是透過Ruby語言所開發出來的 Web Framework,也是採用 Model-View-Controller 架構。Model 部份使用 Active Record 概念實做,加上 Migration 機制,使得其 Model 結構非常容易控制。

Python

Python 有許多的 MVC 架構。最常用的有 DjangoTurboGears

JavaScript

PHP

ActionScript 3

參考

參考文獻

外部連結

Wikiwand in your browser!

Seamless Wikipedia browsing. On steroids.

Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.

Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.