MVC思想及其优势

来源:互联网 发布:c 网络编程 编辑:程序博客网 时间:2024/05/05 02:16

《Struts 2.1权威指南》第1章Struts 2概述,本章大致介绍了Web应用的开发历史,从历史的角度介绍了Model 1和Model 2的简要模型和特征,进而介绍了MVC模式的主要策略和主要优势。本节为大家介绍MVC思想及其优势。

1.1.2  Model 1和Model 2

对于Java阵营的动态Web编程技术而言,则经历了所谓的Model 1和Model 2时代。

所谓Model 1就是JSP大行其道的时代,在Model 1模式下,整个Web应用几乎全部由JSP页面组成,JSP页面接收处理客户端请求,对请求处理后直接做出响应。用少量的JavaBean来处理数据库连接、数据库访问等操作。图1.4显示了Model 1的程序流程:

Model 1模式的实现比较简单,适用于快速开发小规模项目。但从工程化的角度来看,它的局限性非常明显:JSP页面身兼View和Controller两种角色,将控制逻辑和表现逻辑混杂在一起,从而导致代码的重用性非常低,增加了应用的扩展性和维护的难度。

早期有大量ASP和JSP技术开发出来的Web应用,这些Web应用都采用了Model 1架构。

 (点击查看大图)图1.4  Model 1的程序流程Model 2已经是基于MVC架构的设计模式。在Model 2架构中,Servlet作为前端控制器,负责接收客户端发送的请求,在Servlet中只包含控制逻辑和简单的前端处理;然后,调用后端JavaBean来完成实际的逻辑处理;最后,转发到相应的JSP页面处理显示逻辑。其具体实现方式如图1.5所示:
 (点击查看大图)图1.5  Model 2的程序流程

正如图1.5中看到的,Model 2下JSP不再承担控制器的责任,它仅仅是表现层角色,仅仅用于将结果呈现给用户,JSP页面的请求与Servlet(控制器)交互,而Servlet负责与后台的JavaBean通信。在Model 2模式下,模型(Model)由JavaBean 充当,视图(View)由JSP页面充当,而控制器(Controller)则由Servlet充当。

由于引入了MVC模式,使Model 2具有组件化的特点,更适用于大规模应用的开发,但也增加了应用开发的复杂程度。原本需要一个简单的JSP页面就能实现的应用,在Model 2中被分解成多个协同工作的部分,需要花更多时间才能真正掌握其设计和实现过程。

Model 2已经是MVC设计思想下的架构,下面简要介绍MVC设计思想的优势。

注意  对于非常小型的Web站点,如果后期的更新、维护工作不是特别大,可以使用Model 1的模式来开发应用,而不是使用Model 2的模式。虽然Model 2提供了更好的可扩展性及可维护性,但增加了前期开发成本。从某种程度上讲,Model 2为了降低系统后期维护的复杂度,却导致前期开发的更高复杂度。


1.1.3  MVC思想及其优势

MVC并不是Java语言所特有的设计思想,也并不是Web应用所特有的思想,它是所有面向对象程序设计语言都应该遵守的规范。

MVC思想将一个应用分成三个基本部分:Model(模型)、View(视图)和Controller(控制器),这三个部分以最少的耦合协同工作,从而提高应用的可扩展性及可维护性。

起初,MVC模式是针对相同的数据需要不同显示的应用而设计的,其整体结构如图1.6所示。

 (点击查看大图)图1.6  MVC结构

在经典的MVC模式中,事件由控制器处理,控制器根据事件的类型改变模型或视图,反之亦然。具体地说,每个模型对应一系列的视图列表,这种对应关系通常采用注册来完成,即:把多个视图注册到同一个模型,当模型发生改变时,模型向所有注册过的视图发送通知,接下来,视图从对应的模型中获得信息,然后完成视图显示的更新。

从设计模式的角度来看,MVC思想非常类似于一个观察者模式,但与观察者模式存在少许差别:观察者模式下观察者和被观察者可以是两个互相对等的对象,但对于MVC思想而言,被观察者往往只是单纯的数据体,而观察者则是单纯的视图页面。

概括起来,MVC有如下特点。

多个视图可以对应一个模型。按MVC设计模式,一个模型对应多个视图,可以减少代码的复制及代码的维护量,一旦模型发生改变,也易于维护。

模型返回的数据与显示逻辑分离。模型数据可以应用任何的显示技术,例如,使用JSP页面、Velocity模板或者直接产生Excel文档等。

应用被分隔为三层,降低了各层之间的耦合,提供了应用的可扩展性。

控制层的概念也很有效,由于它把不同的模型和不同的视图组合在一起,完成不同的请求。因此,控制层可以说是包含了用户请求权限的概念。

MVC更符合软件工程化管理的精神。不同的层各司其职,每一层的组件具有相同的特征,有利于通过工程化和工具化产生管理程序代码。

相对于早期的MVC思想,Web模式下的MVC思想则又存在一些变化,因为对于一个应用程序而言,我们可以将视图注册给模型,当模型数据发生改变时,即时通知视图页面发生改变;而对于Web应用而言,即使将多个JSP页面注册给一个模型,当模型发生变化时,模型无法主动发送消息给JSP页面(因为Web应用都是基于请求/响应模式的),只有当用户请求浏览该页面时,控制器才负责调用模型数据来更新JSP页面。

注意  MVC思想与观察者模式有一定的相似之处,但并不完全相同。经典的MVC思想与Web应用的MVC思想也存在一定的差别,引起差别的主要原因是因为Web应用是一种请求/响应模式下应用,对于请求/响应应用,如果用户不对应用发出请求,视图无法主动更新自己。

1.2  Struts 2的起源和背景
Struts 2以WebWork优秀的设计思想为核心,吸收了Struts 1的部分优点,建立了一个兼容WebWork和Struts 1的MVC框架,Struts 2的目标是希望可以让原来使用Struts 1、WebWork的开发人员,都可以平稳过渡到使用Struts 2框架。
1.2.1  Struts 1简介及存在的问题
从过去的岁月来看,Struts 1是所有MVC框架中不容辩驳的胜利者,不管是市场占有率,还是所拥有的开发人群,Struts 1都拥有其他MVC框架不可比拟的优势。Struts 1的成功得益于它丰富的文档、活跃的开发群体。当然,Struts 1是世界上第一个发布的MVC框架:Struts 1.0在2001年6月发布,这一点可能是使它得到如此广泛拥戴的主要原因。
为了使读者可以明白Struts 1的运行机制,下面将简要介绍Struts 1的基本框架。
Struts 1框架以ActionServlet作为核心控制器,整个应用由客户端请求驱动。当客户端向Web应用发送请求时,请求将被Struts 1的核心控制器ActionServlet拦截,ActionServlet根据请求决定是否需要调用业务逻辑控制器处理用户请求(实际上,业务逻辑控制器还是控制器,它只是负责调用模型来处理用户请求),当用户请求处理完成后,其处理结果通过JSP呈现给用户。
对于整个Struts 1框架而言,控制器就是它的核心,Struts 1的控制器由两个部分组成:核心控制器和业务逻辑控制器。其中核心控制器就是ActionServlet,由Struts 1框架提供;业务逻辑控制就是用户自定义的Action,由应用开发者提供。
对于大部分用户请求而言,都需要得到服务器的处理。当用户发送一个需要得到服务器处理的请求时,该请求被ActionServlet拦截到,ActionServlet将该请求转发给对应的业务逻辑控制器,业务逻辑控制器调用模型来处理用户请求;如果用户请求只是希望得到某个URL资源,则由ActionServlet将被请求的资源转发给用户。
Struts 1的程序运行流程如图1.7所示。
图1.7  Struts 1的程序运行流程 下面就Struts 1程序流程具体分析MVC中的三个角色。
(1)Model部分
Struts 1的Model部分主要由底层的业务逻辑组件充当,这些业务逻辑组件封装了底层数据库访问、业务逻辑方法实现。实际上,对于一个成熟的企业应用而言,Model部分也不是一个简单的JavaBean所能完成的,它可能是一个或多个EJB组件,可能是一个WebService服务。总之,Model部分封装了整个应用的所有业务逻辑,但整个部分并不是由Struts 1提供的,Struts 1也没有为实现Model组件提供任何支持。
(2)View部分
Struts 1的View部分采用JSP实现。Struts 1提供了丰富的标签库,通过这些标签库可以最大限度地减少脚本的使用。这些自定义的标签库可以输出控制器的处理结果。
虽然Struts 1提供了与Ties框架的整合,但Struts 1所支持的表现层技术非常单一:既不支持FreeMarker、Velocity等模板技术,也不支持JasperReports等报表技术。
(3)Controller部分
Struts 1的Controller由两个部分组成。
 系统核心控制器:由Struts 1框架提供,就是系统中的ActionServlet。
 业务逻辑控制器:由Struts 1框架提供,就是用户自己实现的Action实例。
Struts 1的核心控制器对应图1.7中的核心控制器(ActionServlet)。该控制器由Struts 1框架提供,继承HttpServlet类,因此可以配置成一个标准的Servlet,该控制器负责拦截所有HTTP请求,然后根据用户请求决定是否需要调用业务逻辑控制器,如果需要调用业务逻辑控制器,则将请求转发给Action处理,否则直接转向请求的JSP页面。
业务逻辑控制器负责处理用户请求,但业务逻辑控制器本身并不具有处理能力,而是调用Model来完成处理。
Struts 1提供了系统所需要的核心控制器,也为实现业务逻辑控制器提供了许多支持。因此,控制器部分就是Struts 1框架的核心。有时候,我们直接将MVC层称为控制器层。
提示  对于任何的MVC框架而言,其实只实现了C(控制器)部分,但它负责用控制器调用业务逻辑组件,并负责控制器与视图技术(JSP、FreeMarker和Velocity等)的整合。
对于Struts 1框架而言,因为它与JSP/Servlet耦合非常紧密,因而导致了许多不可避免的缺陷,随着Web应用的逐渐扩大,这些缺陷逐渐变成制约Struts 1发展的重要因素——这也是Struts 2出现的原因。下面具体分析Struts 1中存在的种种缺陷。
(1)支持的表现层技术单一
Struts 1只支持JSP作为表现层技术,不提供与其他表现层技术,例如Velocity、FreeMarker等技术的整合。这一点严重制约了Struts 1框架的使用,对于目前的很多Java EE应用而言,并不一定使用JSP作为表现层技术。
虽然Struts 1处理完用户请求后,并没有直接转到特定的视图资源,而是返回一个ActionForward对象(可以理解ActionForward是一个逻辑视图名),在struts-config.xml文件中定义了逻辑视图名和视图资源之间的对应关系,当ActionServlet得到处理器返回的ActionForword对象后,可以根据逻辑视图名和视图资源之间的对应关系,将视图资源呈现给用户。
从上面的设计来看,不得不佩服Struts 1的设计者高度解耦的设计:控制器并没有直接执行转发请求,而仅仅返回一个逻辑视图名——实际的转发放在配置文件中进行管理。但因为Struts 1框架出现的年代太早了,那时候还没有FreeMarker、Velocity等技术,因而没有考虑与这些FreeMarker、Velocity等视图技术的整合。
提示  Struts 1已经通过配置文件管理逻辑视图名和实际视图之间的对应关系,只是没有做到让逻辑视图名可以支持更多的视图技术。
虽然Struts 1有非常优秀的设计,但由于历史原因,它没有提供与更多视图技术的整合,这严重限制了Struts 1的使用。
(2)与Servlet API严重耦合,难于测试
因为Struts 1框架是在Model 2的基础上发展起来的,因此它完全是基于Servlet API的,所以在Struts 1的业务逻辑控制器内,充满了大量的Servlet API。
看下面的Action代码片段:
//业务逻辑控制器必须继承Struts 1提供的Action类
public class LoginAction extends Action
{
//处理用户请求的execute方法
 public ActionForward execute(ActionMapping mapping, ActionForm form,
 HttpServletRequest request, HttpServletResponse response)throws
        AuctionException
 {
//获取封装用户请求参数的ActionForm对象
//将其强制类型转换为登录用的ActionForm
LoginForm loginForm = (LoginForm)form;
//当用户名为scott,密码为tiger时返回成功
if ("scott".equals(loginForm.getUsername()
&& "tiger".equals(loginForm.getPassword())
{
//处理成功,返回一个ActionForward对象
return mapping.findForward("success");
}
else
{
//处理失败,返回一个ActionForward对象
return mapping.findForward("success");
}
 }
}
当我们需要测试上面Action类的execute方法时,该方法有4个参数:ActionMapping、ActionForm、HttpServletRequest和HttpServletResponse,初始化这4个参数比较困难,尤其是HttpServletRequest和HttpServletResponse两个参数,通常由Web容器负责实例化。
因为HttpServletRequest和HttpServletResponse两个参数是Servlet API,严重依赖于Web服务器。因此,一旦脱离了Web服务器,Action的测试非常困难。
(3)代码严重依赖于Struts 1 API,属于侵入式设计
正如从上面代码片段中所看到的,Struts 1的Action类必须继承Struts 1的Action基类,实现处理方法时,又包含了大量Struts 1 API:如ActionMapping、ActionForm和ActionForward类。这种侵入式设计的最大弱点在于,一旦系统需要重构时,这些Action类将完全没有利用价值,成为一堆废品。
可见,Struts 1的Action类这种侵入式设计导致了较低的代码复用。
0 0
原创粉丝点击