ASP.NET MVC 是一个全新的Web应用框架
MVC在20世纪70年代后期出现,产生于Xerox PARC施乐公司的帕洛阿尔托研究中心的Smalltalk项目,当时将其构想为早期GUI应用程序的一种组织方式。最初的MVC模型的某些细节依赖于Smalltalk特有的概念,如屏幕和工具等,但更广泛的概念仍然适用于现在的应用程序,特别适用于Web应用程序。
与MVC应用程序的交互遵循着用户动作和视图更新的自然周期,在此周期中,假设视图是无状态的,这与支撑Web应用程序的HTTP请求和响应方式非常吻合。
进一步地,MVC强制关注分离(Separation of Concerns) - 域模型与控制器逻辑与UI用户界面是松耦合关系。在一个Web应用程序中,这意味着凌乱的HTML与应用程序的其他部分是分离开来的,从而使维护与测试更加简单容易。这导致Ruby on Rails称为复兴的MVC主流,并使其称为MVC模型的实现模板。自MVC出现并显现出优势后,许多其他MVC框架也相继出现。
从高级术语上说,MVC模式意味着一个MVC应用程序将被分离成至少3个部分。
MVC架构的每个部分都是定义良好和自包含的,这称为关注分离。模型中操作数据的逻辑仅包含在模型中,显示数据的逻辑仅包含着在视图中,处理用于请求和用户输入的代码仅包含在控制器中。利用各部分之间清晰的分离,无论应用程序有多大,在其整个生命周期中都会更易于维护和扩充。
对于面向最终用户的应用来说,需要具有一个与用户进行交互的可视化UI界面,又称为视图(View)。
在早期倾向于将所有与UI相关的操作糅合在一起,这些操作包括UI界面呈现、用户交互操作的捕获与响应、业务流程的执行、对数据的存取等。这种设计模式被称为自治视图(Autonomous View,AV)。
典型的人机交互应用具有3个主要的关注点:数据在可视化界面上的呈现、UI处理逻辑(用于处理用户交互式操作的逻辑)、业务逻辑。自治视图模式将三者混合在一起,势必带来问题。
业务逻辑是与UI无关的,应该最大限度地被重用。将业务逻辑定义在自治视图中,相当于将它完全与视图本身绑定在一起。若将UI的行为抽象出来,基于抽象化UI的处理逻辑也是可以被共享的,但是定义在自治视图中的UI处理逻辑也完全丧失了重用的可能。
业务逻辑具有最强的稳定性,UI处理逻辑次之,可视化界面上的呈现最差。将不同稳定性的元素混合一体,具有最差稳定性的元素决定了整体的稳定性,这是“短板理论”在软件设中的体现。
任何涉及UI的组件都不易测试,因为UI是呈现给人看的,并且会与人进行交互,用机器来模拟对组件实施自动化测试本身就不是一件容易的事。
为了解决自治视图导致的问题,采用“关注点分离”(Seperation of Concerns, SoC)的原则将可视化界面呈现、UI处理逻辑、业务逻辑三者分离,并采用合理的交互方式将他们之间的依赖降到最低,即MVC。
MVC的创建者挪威计算机专家,奥斯陆大学名誉教授 Trygve M. H. Reenskau。MVC是他在1979年访问施乐帕克研究中心(Xerox Palo Alto Research Center, Xerox PARC)期间提出的一种针对GUI应用的软件架构模式。
MVC体现了“关注点分离”设计方针,将人机交互引用设计的功能分为Model、Controller、View三部分,各司其职。
Model是对应用状态和义务功能的封装,可理解为同时包含数据和行为的领域模型(Domain Model)。
Model接受Controller的请求并完成相应的义务处理,在应用状态改变时向View发出相应的通知。
View实现可视化界面的呈现并捕获最终用户的交互操作,View捕获到的用户交互操作后会直接转发给Controller,后者完成相应的UI逻辑。如果需要涉及业务功能的调用,Controller会直接调用Model。在完成UI处理之后,Controller会根据需要控制原View或创建新的View对用户交互操作予以响应。
传统的MVC很多人会认为Controller仅仅是View和Model之间的中介,实则不然,View和Model之间存在直接联系,View不仅可以直接调用Model查询其状态信息,当Model的状态发生改变的时候,也可以直接通知View。例如股价实时价位的应用中维护股价的信息的Model,在股价变化的情况下可直接通知相关的View改变其显示状态。
从消息交互模式的角度来讲,无论是Model在应用状态发生改变时通知View,还是View在捕获到用户交互操作后通知Controller,消息都是以“单向(One-Way)”方式流动的,推荐采用事件机制来实现两种类型过的通知。
从设计角度来讲,采用观察者模式(Observer)通过注册/订阅的方式来实现,具体来讲就是让View作为Model的观察者,通过注册相应的事件来检测状态的改变。让Controller作为View的观察者,通过注册相应的事件来处理用户的交互操作。
MVC和所谓的“三层架构”两者没有什么可比性,MVC更不是分别对应UI、业务逻辑、数据存取三个层次。Trygve M. H. Reenskau提出MVC是将其作为构建整个GUI应用的架构模式,它更多地体现为一个领域模型。对于多层架构来说,MVC是被当成UI呈现层(Presentation Layer)的设计模式,Model则更多体现为访问业务层的入口(Gateway)。
采用MVC范例将可视化UI元素的呈现、UI处理逻辑、业务逻辑分别定义在View、Controller、Model中。MVC并没有对三者之间的交互进行严格的限制,主要体现在它允许View和Model绕开Controller进行直接交互,不仅View可以通过调用Model获取需要呈现给用户的数据。Model也可以直接通知View让其感知到应用状态的变化。将MVC范例应用于具体的项目开发时,不论是基于GUI的桌面应用或是基于浏览器的Web应用,如果不多MVC之间的交互作更为严格的约束,编写的程序可能比自治视图更加难以维护。
现在MVC被视为一种模式(Pattern),而最初的提出者却将其视为一种范例(Paradigm)。模式和范例的区别在于,模式可以直接在具体的应用上使用,范例仅仅提供基本指导方针。
软件设计的发展历程中出现了MVC的变体(Variation),它们遵守定义在MVC中的基本原则,但对于三者之间的交互制定了更为严格的规范。
“模型-视图-呈现器(Model-View-Presenter, MVP)”是MVC的一种变异,以便更容易地适应状态化的GUI平台,如Windows Form或ASP.NET Web Form。
MVP模式中,呈现器P具有与MVC控制器同样的职责,但它与状态化视图有更直接的关系,根据用户的输入和动作,直接管理着UI组件中显示的数据。
该模式有以下两种实现:
这两种实现之间的差别涉及视图如何智能化,任何一种方式,呈现器与GUI框架都解耦的,这使得呈现器逻辑简单且易于单元测试。
MVP是一种广泛使用的UI架构模式,适用于基于事件驱动的应用框架,比如ASP.NET Web Forms和Windows Forms应用。
MVP中的M和V分别对应于MVC的Model和View,而P(Presenter)则自然代替了MVC中的Controller。MVP并非仅仅体现在从Controller到Presenter的转换,更多地体现在Model、View、Presenter之间的交互上。
MVC范例中三元素之间混乱的交互主要体现在允许View和Model绕开Controller进行单独交流,这个问题在MVP中得到了彻底解决。
能够与Model直接进行交互的仅限于Presenter,View只能通过Presenter间接地调用Model。Model的独立性在这里得到了真正的体现,它不仅仅与可视化元素的呈现(View)无关,与UI处理逻辑(Presenter)也无关。使用MVP的应用是用户驱动而非Model驱动的,所以Model无需主动通知View以提醒状态发生了改变。
MVP不仅避免了View和Model之间的深度耦合,更进一步降低了Presenter对View的依赖。Presenter依赖的是一个抽象化的View,即具体View实现的接口IView,这带来的最直接的好处就是使定义在Presenter中的UI处理逻辑变得更易于测试。由于Presenter对View的依赖行为定义在接口IView中,仅需Mock一个实现了该接口的View就能对Presenter进行测试。
MVP三要素的交互主要体现在:View与Presenter、Presenter与Model之间的交互。Presenter和Model之间的交互很清晰,仅仅体现为Presenter对Model的单向调用。View和Presenter之间采用怎样的交互式整个MVP的核心。MVP针对关注点分离的初衷能否体现在具体的应用中,很大程序上取决于两者之间的交互方式是否正确。
按照View和Presenter之间的交互方式,以及View本身的职责范围,Martin Folwer将MVP分为PV(Passive View)和SC(Supervising Controller)两种模式。
“模型-视图-视图模型(MVVM, Model-View-View Model)”模式是MVC的最新变异,源于微软并被应用于WPF。在MVVM模型中,模型和视图具有与MVC同样的作用。所不同的是MVVM中关于视图模型的概念,是用户界面的一种抽象表示 -- 典型地是一个C#类,它即暴露了视图中待显示数据的模型属性,也暴露了能够通过UI进行调用的对数据的操作。与MVC的控制器不同,MVVM视图模型没有视图或任何特定UI技术的观念。MVVM视图使用WPF的绑定(Binding)特性,将视图控件所暴露的属性与视图模型所暴露的属性双向地关联在一起。
MVC最初作为桌面应用的架构模式并不太适合Web,Web应用于桌面应用的主要区别在于用户是通过浏览器与应用进行交互,交互请求和响应是通过HTTP请求和响应来完成的。
为了让MVC能够为Web应用提供原生的支持,Java阵营提出了Model2的Web架构模式。JavaWeb具有两种基于MVC的架构模式,分别称之为Model1和Model2,Model1类似自治视图模式。
为了让开发者采用相同的编程模式进行桌面应用和Web应用开发,微软通过ViewState和Postback对HTTP请求和响应机制进行了封装,能够像编写WindowsForms应用一样采用事件驱动的方式进行ASP.NET Web Forms应用的编程。Models则采用完全不同的设计,让开发者直接面向WEB,关注HTTP的请求和响应,所以Model2提供对Web引用原生的支持。
对于Web应用来说,和用户直接交互的UI界面由浏览器来呈现,用户交互请求通过浏览器以HTTP请求的方式发送到Web服务器,服务器对请求进行相应的处理并最终返回一个HTTP回复对请求予以响应。
Model2中一个HTTP请求的目标是Controller中某个Action,具体体现为定义在Controller类型的某个方法,所以对请求的处理最终体现在对目标Controller对象的激活和对目标Action方法的执行。一把来说,Controller的类型和Action方法的名称及作为Action方法的部分参数可直接通过请求的URL解析出来。
通过拦截器(Interceptor)对抵达Web服务器的HTTP请求进行拦截,Web应用框架都提供了这样的拦截机制。对于ASP.NET来说,通过HttpModule的形式来定义这样的拦截器。拦截器根据当前请求解析出目标Controller的类型和对应的Action方法的名称,随后目标Controller被激活,相应的Action方法被执行。
目标Action方法被执行过程中,可调用Model获取相应的数据或改变其状态。在Action方法执行的最后阶段一般会创建一个View,后者最终被转换成HTML以HTTP响应的形式返回到客户端并呈现在浏览器中,绑定在View上的数据来源于Model或基于显示要求进行简单逻辑计算,有时将其称为VM(View Model),即基于View的Model。
ASP.NET MVC 与 Model2
ASP.NET MVC是根据Model2模式设计的,对HTTP请求进行拦截以实现对目标Controller和Action名称的解析式通过一个自定义HttpModule来实现的,目标Controller的激活和Action方法的执行是通过一个自定义HttpHandler来完成的。
MVC的Model主要体现为维持应用状态并提供业务功能的领域模型,或者是多层架构中进入业务层的入口合伙业务服务的代理,但是ASP.NET MVC中的Model还是这个Model吗?ASP.NET MVC的Model仅仅是绑定到View上的数据而已,和MVC中的Model并不是一回事儿。由于ASP.NET MVC中的Model是服务于View的,可见其成为ViewModel。
由于ASP.NET MVC只是View Model,所以ASP.NET MVC应用框架本身仅仅关注View和Controller,真正的Model以及Model和Controller直接的交互体现在如何来设计Controller。
在MVC中,控制器是C#类,派生自System.Web.Mvc.Controller
类。从这个Controller
派生而来的类中,每个public
方法都称为一个动作方法(Action Method),这种动作方法通过ASP.NET的路由系统与一个可配置的URL相关联。当一个请求被发送给一个动作方法相关联的URL时,便会执行控制器中的语句,以进行域模型上的一些操作,然后选择一个视图显示给客户端。
ASP.NET MVC框架使用视图引擎(ViewEngine), 该引擎是负责处理视图的组件,以便为浏览器生成响应。MVC的早期版本使用标准的ASP.NET视图引擎,使用改进的WebForm标记语法来处理ASPX页面。
MVC与其他模式比较
智能UI最大的缺点是难以维护和扩展,域模型与带有用户界面代码的业务逻辑混搭在一起,导致了重复。在MVC中,智能UI被认为是一种抵抗模式(Anti-pattern),是不屑一切代价应该避免的。
2.模式-视图架构
模型-视图(Model-View)架构提供了一种改进智能UI的方法,将事务逻辑才抽取形成能独立的域模型。数据、过程、规则被自己集中在应用程序的部件中。不过带来的问题是:UI与域模型结合得十分紧密,对每部分的单元测试都和困难,其次问题来自于实际情况而非模式的定义。模型含有大量数据访问代码,这意味着,数据模型并不仅仅只包含事务数据、操作和规则。
三层架构是事务性应用能程序使用最广泛的模式,对如何实现UI没有约束,并提供了较好的关注分离且又不太复杂。某些情况下DAL能够使单元测试相对容易。