转载自:http://www.cnblogs.com/baiye7223725/archive/2007/06/07/775390.aspx
摘要:本文主要论述了MVC架构的原理、优缺点以及MVC所能为Web应用带来的好处。并以“成都市信息化资产管理系统”框架设计为例,详细介绍其在Asp.net环境下的具体实现。旨在帮助Web设计开发者更好的了解和掌握MVC,合理利用MVC构建优秀的Web应用。
关键字:MVC、视图、控制器、模型、Asp.net
Application and Implementation of MVC Construction in Asp.net
Abstract: This article mainly elaborated the construction principle of MVC, the merits and shortcoming as well as MVC can be an advantage, which the Web application brings. And take “the Chengdu informationization property management system management system” the frame design as the example, introduces its concrete realization in detail under Asp. net environment. Is for the purpose of helping Web to design an exploiter better understanding and to grasp MVC, reasonably constructs the outstanding Web application using MVC.
Keywords: MVC 、View 、Controller、Model、Asp.net
0 引言
许多Web应用都是从数据存储检索数据并将其显示给用户。在用户更改数据之后,系统再将更新内容存储到数据存储中。因为关键的信息流发生在数据存储和用户界面之间,所以很多应用将数据和用户界面这两部分绑在一起,以减少编码量并提高应用程序性能。但是,这种看起来自然而然的方法有一些大问题。一是,用户界面的更改往往比数据存储系统的更改频繁得多。二是,这种耦合往往会并其他业务逻辑。那么如何让 Web 应用程序的用户界面功能实现模块化,以便可以轻松地单独修改各个部分呢?面向对象的设计模式是经验的总结,MVC架构可以很好地解决上述问题。
.NET是当今设计和开发各种Web应用的主流平台,MVC架构在J2EE平台上已有成熟的设计方案,而在.NET平台上却少有应用。所以讨论其在Asp.net环境下的应用和实现,仍很有意义。
本文首先论述了MVC架构的原理、优缺点以及它所能为Web应用带来的好处。并结合作者在“成都微软技术中心”实习期间,研发项目的经验。介绍了一种在Asp.net环境下的实现方式。旨在帮助Web设计开发者更好的了解和掌握MVC,合理利用MVC构建优秀的Web应用。虽然本文是在.net环境下的实现,但这并不妨碍你对MVC架构的理解。学习MVC架构,重在学习其思想。
1 MVC介绍
MVC是一种软件开发架构,它包含了很多的设计模式[1],最为密切是以下三种:Observer (观察者模式), Composite(组合模式)和Strategy(策略模式)。MVC最初是在Smalltalk-80中被用来构建用户界面的[2]。
MVC架构把数据处理,程序输入输出控制及数据显示分离开来,并且描述了不同部件的对象间的通信方式。使得软件可维护性,可扩展性,灵活性以及封装性大大提高;MVC(Model-View-Controller)把系统的组成分解为M(模型)、V(视图)、C(控制器)三种部件。视图表示数据在屏幕上的显示。控制器提供处理过程控制,它在模型和视图之间起连接作用。控制器本身不输出任何信息和做任何处理,它只负责把用户的请求转成针对Model的操作,和调用相应的视图来显示Model处理后的数据。三者之间关系如下图2.1:
图2.1 MVC关系图
2、为什么要在Web应用中使用MVC架构
用户界面逻辑的更改往往比业务逻辑频繁,尤其是在基于Web的应用程序中。例如,可能添加新的用户界面页,或者可能完全打乱现有的页面布局。对显示的更改,尽可能地不要影响到数据和业务逻辑。
目前大部分Web应用都是将数据代码和表示混在一起。经验比较丰富的开发者会将数据从表示层分离开来,但这通常不是很容易做到的,它需要精心的计划和不断的尝试。MVC从根本上强制性的将它们分开。尽管构造MVC应用需要一些额外的工作,但它带来的好处是无庸质疑的。
2.1 提高代码重用率
最重要的一点是多个视图能共享一个模型,无论用户想要Flash界面或是 WAP 界面;用一个模型就能处理它们。由于已经将数据和业务规则从表示层分开,所以可以最大化的重用代码。
2.2 提高程序的可维护性
因为模型是自包含的,并且与控制器和视图相分离,所以很容易改变数据层和业务规则[3]。例如,把数据库从MySQL移植到Oracle,或者把基于RDBMS数据源改变到LDAP,只需改变模型即可。一旦正确的实现了模型,不管数据来自哪里,视图都会正确的显示它们。MVC架构的运用,使得程序的三个部件相互对立,大大提高了程序的可维护性。
2.3 有利于团队开发
在开发过程中,可以更好的分工,更好的协作。有利于开发出高质量的软件。良好的项目架构设计,将减少编码工作量 :采用MVC结构 + 代码生成器,是大多数Web应用的理想选择。部分模型(Model)、和存储过程一般可用工具自动生成。控制(Controller)器比较稳定,一般由于架构师(也可能是有经验的人)完成;那么整个项目需要手动编写代码的地方就只有视图(View)了。在这种模式下,个人能力不在特别重要,只要懂点语法基础的人都可以编写,无论项目成员写出什么样的代码,都在项目管理者的可控范围内。即使项目中途换人,也不会有太大问题。在个人能力参差不齐的团队开发中,采用MVC开发是非常理想的。
3 MVC在 Asp.net中的原理及实现
Asp.net提供了很好实现这种模式的类似环境。通过在ASPX页面中开发用户部件或继承母板页MasterPage来实现视图;控制器的功能一般可以放在对应的逻辑功能代码(.cs)中实现;模型通常对应应用系统的业务部分。模型一般包含业务逻辑、业务规则和数据访问层。MVC可和经典的N层结构配合使用。将用户显示(视图)从动作(控制器)中分离出来,提高了代码的重用性。将数据(模型)从对其操作的动作(控制 器)分离出来可以设计一个与后台存储数据无关的系统。就MVC结构的本质而言,它是一种解决耦合系统问题的方法[4]。实现基于MVC的应用需要完成以下步骤,如右图3.1所示: |
1、分析当前应用,分解系统功能:
分析当前应用问题,分离出系统的内核功能(Model)、系统的输入输出(View)、系统的输流程控制,行为控制等控制功能(Controller)三大部分。
2、设计和实现模型:
设计模型部件使其封装应用功能、属性。提供访问显示数据的操作,提供控制内部行为的操作以及其他必要的操作接口。这部分的构成与具体的应用问题紧密相关。
3、设计和实现视图:
设计每个视图的显示形式,视图从模型中获取数据,并将数据显示在屏幕上。提供发送用户请求给控制器;提供允许控制器选择视图。
4、设计和实现控制器:
对于每个视图,实现对用户的请求映射到模型。并根据模型处理结果,选择合适的视图显示。在模型状态的影响下,控制器使用特定的方法接受和解释这些事件。控制器的初始化建立起与模型和视图的联系,(这里一般会用观察者模式)并且启动事件处理机制。事件处理机制的具体实现方法依赖于界面的工作平台。
MVC并没有明确的定义,它仅代表一种软件设计思想。所以在不同的应用环境下,可能有不同的实现方式。只有深刻理解其思想,结合实际情况。才能构建合理的应用。下面以“成都市信息化资产管理系统”框架设计为例,介绍MVC构架在Asp.net下的一种实现方式。该框架中并没有使用观察者模式,因为依赖关系(本项目中只有两种视图,列表页面和编辑、查看详情页面。而且将来增加视图的可能性也不大)固定或者几乎固定时,加入一个观察者模式,只会增加系统复杂性。
本项目框架结构包括逻辑结构图3.2和物理结构图3.3两部分。
从逻辑结构图,可以看出对数据库的访问并没有完全用存储过程,这是出于运行效率和开发效率的考虑。
这里的存储过程对每个实体都只包括基本的CRUD四种操作。
3.1 View(视图)
3.1.1原理
视图用于管理信息的显示,它提供用户交互界面。使用多个包含单页面显示的用户部件,复杂的Web页面可以展示来自多个数据源的内容,并且网页人员,美工能独自参与这些Web页面的开发和维护。在Asp.net下,视图的实现很简单。可以像开发WINDOWS界面一样直接在集成开发环境下通过拖动部件来完成页面开发本。每一个页面也可以采用复合视图的形式即:一个页面由多个子视图(用户部件)组成;也可以继承母板页MasterPage。子视图可以是最简单HTML 部件、服务器部件或多个部件嵌套构而成的Web自定义部件或Web页面。 页面都由模板定义,模板定义了页面的布局,用户部件的标签和数目,用户指定一个模板(这里的模板指Html页面、Asp.net页面、用户部件等),.net平台根据这些信息自动创建页面。针对静态的模板内容,如页面上的站点 导航,菜单,友情链接,这些使用缺省的模板内容配置;针对动态的模板内容(主要是业务内容),由于用户的请求不同,只能使用后期绑定,并且针对用户的不同,用户部件的显示内容进行过滤。使用由用户部件根据模板配置组成的组合页面,它增强了可重用性,并简化了站点的布局。在.Asp.net2.0中,可以使用MasterPage来简化视图设计。在MasterPage里设置的Skin(皮肤),会根据不同子视图(继承自MasterPage页)中的Them(主题)。自动选择合适的Skin显示。可以说MasterPage是MVC架构思想的很好体现。
视图部分大致处理流程如下:首先,页面模板定义了页面的布局;页面配置文件定义视图标签的具体内容(用户部件);然后,由页面布局策略类初始化并加载页面;每个用户部件根据它自己的配置进行初始化,加载校验器并设置参数,以及事件的委托等;用户提交后,通过了表示层的校验,用户部件把数据自动提交给业务实体即模型。
这一部分主要定义了WEB页面基类PageBase;页面布局策略类PageLayout,完成页面布局,用于加载用户部件到页面;用户部件基类 UserControlBase即用户控件框架,用于动态加载检验部件,以及实现用户部件的个性化。为了实现WEB应用的灵活性,视图部分也用到了许多配置文件例如:模板配置、页面配置、路径配置、验证配置等。
3.1.2实现
良好的界面架构设计,将减少界面调整时间。在.net下应充分利用Asp.net2.0新特性,自动导航,SiteMap、MasterPage、MemberShip、MultiView、Them、Skin等。在本项目中,每个模块的View,实际上都只有两种,一种是用来显示多条数据的列表页面,一种是用来编辑、和查看详情的页面。由于View种类几乎是固定的,所以不需要加入Observer(观察者)模式。让所有的编辑页都继承自“母板页dialog.master”,所有列表页都继承“母板页Main.master”即可。如图3.4
每个列表页面的动态显示区域仅为ContentPlaceHolder即黄色区域部分,这就保证相同类型页面风格的一致。按照命名规范和便于理解的原则,我们把所有的编辑页后缀都取名为“EditPG.aspx”,所有的列表页后缀都取名为“ListPG.aspx”。对View的改变,可以通过Asp.net2.0的主题(Themes)来实现。本例中编辑和查看详情页面,用的就是相同的视图(View)。如果要添加不同的View,只需添加相应的Master,和完成具体的显示要求。在本项目中,所有的编辑页面一般只需要实现基类(DialogUIBase)提供的如下方法。
protected override void GetDataFromDB(object keyValue){…}
//用Model中的数据填充编辑或显示界面
protected override void SetEditText(){…}
//重新填写编辑框页面的部件内容, 对部分用户可能重复填写的部件内容不进行赋默 认值操作
protected override void ResetEditText(){…}
//检查用户输入正确性
protected override string CheckUserInput(){…}
//保存用户输入,把用户输入更新到数据库
protected override object SaveEditText(object keyValue){…}
所有的列表View一般也只用实现基类(GridViewUIBase)提供的如下几个虚方法.
//返回子类中使用的GridView,子类必须继承
protected override GridView GetGridView(){…}
// 返回子类中的GridView中复选框列模板中复选框的名称,子类根据有无该模板列进 行选择继承
protected override string GetGridCheckBoxName(){…}
通常无需处理的继承方法
//绑定DataGrid部件事件,
protected override void BindGridEvent()
//绑定除通过GetDataGrid()传入的DataGrid部件以外的部件的客户端事件
protected override void BindControlEvent()
// 返回GridViewList使用的数据源, 子类必须继承
protected override object GetDataSource()
// 多条记录删除,在删除按钮事件中调用
protected override void DelRecords(object keyValuesString)
// 返回以CommandName为key以GridEventPageParam类型参数为内容的hashtable
protected override Hashtable GetDialogParams(){…}
从以上代码中可以很容易发现,无论是列表页面还是编辑页面,都没有和流程相关的东西,这正是MVC所要做的,View中只包含数据的显示,流程完全由基类控制。好处是显而易见的。不同的人写出来的View也具有相同的风格。
3.2 Controller(控制器)
3.2.1原理
Controller控制器是Model与View之间沟通的桥梁,它可以分派用户的请求并选择恰当的视图以用于显示,同时它也可以解释用户的输入并将它们映射为模型层可执行的操作。在.NET中每个aspx对应了一个后端代码aspx.cs,可以通过aspx.cs方便地实现Controller的功能。每个Asp.net页面都有一种机制,将页面中的部件所要调用的方法在一个与其分离的类中实现。这些aspx和ascx文件后端代码继承了System.Ul.Web.Page的类执行控制器功能,它包括了各种初始化和控制函数。当加载aspx页面时将调用Page_ Load事件,当aspx页面从内存中被卸载时将调用Page_UnLoad事件。如果某个部件触发页面以使其被重新加载则将调用Control Event事件。
3.2.2实现
对应所有编辑页面的控制类为DialogUIBase.cs ,该类完成所有编辑页面的流程控制、请求控制 ;对应所有列表页面的控制类为GridViewUIBase.cs ,该类完成所有列表页面的流程控制和请求控制 ;这两个类都位于App_Code文件夹下。
编辑页面基类(DialogUIBase)和列表页面基类(GridViewUIBase)都继承自System.Web.UI.Page,都包含两部分,一是供View子类继承的虚方法,一是对View子类流程控制的方法。
本例中GridViewUIBase中主要包含的方法有:
//返回子类中使用的GridView
protected virtual GridView GetGridView(){…}
//返回子类中的GridView中复选框列模板中复选框的名称,子类根据有无该模板列进行选择继承
protected virtual string GetGridCheckBoxName(){…}
// 返回GridView使用的数据源
protected virtual object GetDataSource(){…}
// 删除View中的选择的数据
protected virtual void DelRecords(object keyValuesString){…}
// 返回以CommandName为key以GridEventPageParam类型参数为内容的hashtable
protected virtual Hashtable GetDialogParams(){…}
// 绑定GridView部件客户端事件,通本默认绑定函数绑定的客户端事件,被绑定列的所有行均调用相同的对话框页面,如果要不同的行调用不同的对话框页面则需要重写该函数
protected virtual void BindGridEvent(){…}
//绑定除通过GetGridView()传入的GridView部件以外的部件的客户端Click事件
protected virtual void BindControlEvent(){…}
//按钮事件绑定
public void BindBtnEvent(…){…}
//表格事件绑定
public void BindGridEvent(…){…}
DialogUIBas类和GridViewUIBase类,设计思路完全相同。所以不再举例。从上面当面可以发现在GridViewUIBase中,实现了对View的控制。根据用户的请求的不同,调用不同的Model进行处理。
3.3 Model(模型)
3.3.1原理
Model对象代表了商业规则和商业数据,单个模型代表问题域中的某个对象,或叫做实体。所以模型要封装系统的应用功能和应用属性。提供访问显示数据的操作,提供控制内部行为的操作以及其他必要的操作接口。模型的构成与具体的应用问题紧密相关。通常模型包括数据访问、商务逻辑和商务规则。在Asp.net中,简单的模型可以方便地用自动代码生成工具实现。VS IDE 2003、VS IDE 2005本身就提供了很好的支持,可以从数据库或XML等数据源,轻松的生成强类型的DataSet和DataTable。数据访问层可以使用Application Block块。或Enterprise Library 等开源组件。当然你也可以手动完成这些工作,如果你愿意。
3.3.2实现
在本示例中,业务处理对象和业务实体对象都继承自EntityBase类。EntityBase类又继承自Entity类。Entity类是数据库访问的基类。它主要包含供子类继承的方法(用存储过程完成数据库的CRUD操作)。和供外部类调用的方法(Model完成CRUD操作)。设置两种方式是因为逻辑结构的需要。
供子类继承的主要方法如下:
protected virtual bool Proc_Insert(){…} //添加
protected virtual bool Proc_Update(object KeyValue) {…} //更新
protected virtual bool Proc_ReadByKeyValue(object KeyValue){…} //检索
protected virtual bool Proc_Delete(object KeyValue){…} //删除
protected virtual DataTable Proc_ReadAll(){…} //检索所有
protected virtual void AfterLoad(){…} //数据库中数据更新模型之前
protected virtual void BeforeSave(){…}//用模型更新数据库之前
供外部调用的主要方法如下:
public DataTable ReadAll(){…} //检索所有
public object Insert(){…} //添加
public object Update(object keyValue){…} //更新
public bool Load(object keyValue){…} //填充Model
public bool Delelte(object keyValue){…} //删除
public void Clear() //清除Model
EntityBase类,只需实现基类(Entity)的四个虚CRUD方法,和定义Model自身相关的属性。由于Model又继承于EntityBase类,所以如果某个Model需要进行额外的操作,可添加到该Model对应的AfterLoad()或BeforeSave()方法中。
3.4 MVC架构的扩展设计
通过在Asp.net中使用MVC模式,可以构建,具有良好扩展性的Web应用。MVC构架可以轻松实现以下功能:
①实现一个模型的多个视图;
②采用多个控制器;
③当模型改变时,所有视图将自动刷新;
④所有的控制器将相互独立工作。
这就是MVC模式的好处,只需在以前的程序上稍作修改或增加新的类,即可轻松增加许多程序功能。以前开发的许多类可以重用,而程序结构根本不再需要改 变,各类之间相互独立,便于团体开发,提高开发效率。下面讨论如何实现一个模型、两个视图和一个控制器的程序。其中模型类及视图类根本不需要改变,与前面的完全一样,这就是面向对象编程的好处。对于控制器中的类,只需要增加另一个视图,并与模型发生关联即可。该模式下视图、控制器、模型三者之间的示意图如图3.5所示。
同样也可以实现其它形式的MVC例如:一个模型、两个视图和两个控制器。从上面可以看出,通过MVC模式实现的应用程序具有极其良好的可扩展性,是Asp.net面向对象编程的未来方向。
4 MVC架构的优点及不足
4.1 MVC的优点
MVC的优点体现在以下几个方面:
(1) 有利于团队开发分工协作和质量控制,降低开发成本。
(2) 可以为一个模型在运行时同时建立和使用多个视图。变化-传播机制可以确保所有相关的视图及时得到模型数据变化,从而使所有关联的视图和控制器做到行为同步。
(3) 视图与控制器的可接插性,允许更换视图和控制器对象,而且可以根据需求动态的打开或关闭、甚至在运行期间进行对象替换。
(4) 模型的可移植性。因为模型是独立于视图的,所以可以把一个模型独立地移植到新的平台工作。需要做的只是在新平台上对视图和控制器进行新的修改。
(5) 潜在的框架结构。可以基于此模型建立应用程序框架,不仅仅是用在设计界面的设计中。
4.2 MVC的缺点
MVC的不足体现在以下几个方面:
(1)增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。
(2)视图对模型数据的访问效率低。视图可能需要多次调用Model才能获得足够的显示数据。
(3)完全理解MVC并不是很容易。使用MVC需要精心的计划,由于它的内部原理比较复杂,所以需要花费一些时间去思考。 同时由于模型和视图要严格的分离,这样也给调试应用程序到来了一定的困难。
结束语
与软件所处理问题的内在模型相比较,用户界面是需要经常发生变化的,采用MVC设计模式可以在满足对界面要求的同时,使软件的计算模型独立于界面的构成。也可以基于此模型建立大型分布式应用程序框架。
MVC并不适合小型甚至中等规模的应用程序,花费大量时间将MVC应用到规模并不是很大的应用程序通常会得不偿失。
MVC是一种软件开发架构。和其它设计模式一样,它不是万能的,也不是一成不变的。要根据具体情况灵活运用。在上面的示例项目中,为了提高运行和开发效率。在Model设计上就提供了两种访问方式。
示例中的MVC采用了集中控制的方式。一个列表控制器GridViewUIBase,对应多个列表视图。一个编辑控制器DialogUIBase对应对个编辑、查看详情视图。对每个模型而言,仅有两种视图,且几乎是固定不变的。所以没有增加Observer(观察者)模式。这样减少了系统的复杂性。本示例最精彩的部分,就是控制器的设计。各视图执行流程完全封装在控制器中。由于视图中不含有任何控制信息,流程信息。所以视图编码人员完全不用了解Http的无状态特性等。对他们而言,开发WebForm和WinForm是一样的。当然这种设计也有它的不足,如果修改某个视图的显示,有可能还要修改相关的控制器。