MVC

一种软件架构设计模式

MVC模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

MVC模式最早由Trygve Reenskaug在1978年提出[1],是施乐帕罗奥多研究中心(Xerox PARC)在20世纪80年代为程序语言Smalltalk发明的一种软件架构。MVC模式的目的是实现一种动态的程式设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式透过对复杂度的简化,使程序结构更加直观。软件系统透过对自身基本部分分离的同时也赋予了各个基本部分应有的功能。专业人员可以依據自身的专长分组:

  • 模型(Model) - 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。
  • 视图(View) - 界面设计人员进行图形界面设计。
  • 控制器(Controller)- 负责转发请求,对请求进行处理。
MVC模式

组件的互动

 
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 平台企业版 (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的确是个不错的选择。

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

ActionScript 3

參考

参考文献

  1. ^ Reenskaug, Trygve. THING-MODEL-VIEW-EDITOR: an Example from a planningsystem (PDF). [2013年4月30日]. (原始内容存档 (PDF)于2017年9月21日). 
  2. ^ Buschmann, Frank (1996) Pattern-Oriented Software Architecture.
  3. ^ ASP.NET MVC 概觀. [2010-11-04]. (原始内容存档于2015-12-18). 

外部链接