UML软件工程组织

从Java类库看设计模式 -- MVC模式
摘自IBM DeveloperWorks

 

有了前面诸多设计模式的基础,这儿可以提出一个比较特殊的模式MVC。MVC并不属于GOF的23个设计模式之列,但是它在GOF的书中作为一个重要的例子被提出来,并给予了很高的评价。一般的来讲,我们认为GOF的23个模式是一些中级的模式,在它下面还可以抽象出一些更为一般的低层的模式,在其上也可以通过组合来得到一些高级的模式。MVC就可以看作是一些模式进行组合之后的结果(实际上,MVC的出现要早于设计模式的提出,这而只是对它在设计模式的基础上进行在分析)。如果没有前面的基础,理解MVC或许会有一些困难。
MVC模式
MVC模式比较的特别,它含义比较的广,涉及的层面也不仅仅是设计这一块,不好简单的把它归为设计模式。当然,它主要还是作为一个设计的概念被提到的,而且在Java体系中,MVC有着至关重要的作用。这儿提的是Java中的设计模式,当然不好拉了它不讲了。

关于MVC的来龙去脉,这儿就不再讲了。这里主s要讲两个方面的:作为设计模式的MVC和作为体系结构模式的MVC。

所谓MVC,指的是一种划分系统功能的方法,它将一个系统划分为三个部分:

模型(Model):封装的是数据源和所有基于对这些数据的操作。在一个组件中,Model往往表示组件的状态和操作状态的方法。

视图(View):封装的是对数据源Model的一种显示。一个模型可以由多个视图,而一个视图理论上也可以同不同的模型关联起来。

控制器(Control):封装的是外界作用于模型的操作。通常,这些操作会转发到模型上,并调用模型中相应的一个或者多个方法。一般Controller在Model和View之间起到了沟通的作用,处理用户在View上的输入,并转发给Model。这样Model和View两者之间可以做到松散耦合,甚至可以彼此不知道对方,而由Controller连接起这两个部分。

有了前面介绍的诸多模式之后,就可以很容易的通过模式来解释MVC的内在行为了。前面说过,在设计模式中,MVC实际上是一个比较高层的模式,它由多个更基本的设计模式组合而成,Model-View的关系实际上是Observer模式,模型的状态和试图的显示相互响应,而View-Controller则是由Strategy模式所描叙的,View用一个特定的Controller的实例来实现一个特定的响应策略,更换不同的Controller,可以改变View对用户输入的响应。而其它的一些设计模式也很容易组合到这个体系中。比如,通过Composite模式,可以将多个View嵌套组合起来;通过FactoryMethod模式来指定View的Controller,等等。

使用MVC的好处,一方面,分离数据和其表示,使得添加或者删除一个用户视图变得很容易,甚至可以在程序执行时动态的进行。Model和View能够单独的开发,增加了程序了可维护性,可扩展性,并使测试变得更为容易。 另一方面,将控制逻辑和表现界面分离,允许程序能够在运行时根据工作流,用户习惯或者模型状态来动态选择不同的用户界面。

Swing号称是完全按照MVC的思路来进行设计的。在设计开始前,Swing的希望能够达到的目标就包括:

模型驱动(Model-Driven)的编程方式。
提供一套单一的API,但是能够支持多种视感(look-and-feel),为用户提供不同的界面。

很自然的可以发现,使用MVC模式能够有助于实现上面的这两个目标。

严格的说,Swing中的MVC实际上是MVC的一个变体:M-VC。 Swing中只显示的定义了Model接口,而在一个UI对象中集成了视图和控制器的部分机制。View和Control比较松散的交叉组合在一起,而更多的控制逻辑是在事件监听者部分引入的。

但是,这并没有妨碍在Swing中体现MVC的精髓。事实上,在Swing的开发初期,Swing确实是按照标准的MVC模式来设计的,但是很快的问题就出现了:View和Controller实际上是紧密耦合的,很难作出一个能够适应不同View的一般化的Controller来,而且,一般也没有很大的必要。

在Swing中基本上每一个组件都会有对应的Model对象。但其并不是一一对应的,一个Model接口可以为多个Swing对向服务,例如:JProgressBar,JScrollBar,JSlider这三个组件使用的都是BoundedRangeModel接口。这种模型的共享更能够从分的体现MVC的内涵。除了Model接口外,为了实现多个视感间的自由切换,每个Swing组件还包含一个UI接口--也就是View-Controller,负责对组件的绘制和接受用户输入。

Model-View是Subject和Obverser的关系,因而,模型的改变必须要在UI对象中体现出来。Swing使用了JavaBeans的事件模型来实现这种通知机制。具体而言,有两种实现办法,一是仅仅通知事件监听者状态改变了,然后由事件监听者向模型提取必要的状态信息。这种机制对于事件频繁的组件很有效。另外的一种办法是模型向监听者发送包含了已改变的状态信息的通知给UI。这两种方法根据其优劣被分别是现在不同的组件中。比如在JScollBar中使用的是第一种方法,在JTable中使用的是第二种方法。而对Model而言,为了能够支持多个View,它并不知道具体的每一个View。它维护一个对其数据感兴趣的Obverser的列表,使得当数据改变的时候, 能够通知到每一个Swing组件对象。

上面讲到的是作为设计模式的MVC。而在J2EE中,Sun更是将MVC提升到了一个体系结构模式的高度,这儿的MVC的含义就更为广泛了。与Swing中不同的是,在这儿MVC的各个部件不再是单纯的类或者接口,而是应用程序的一个组成部分!

在J2EE Blueprint中,Sun推荐了一种基于MVC的J2EE程序的模式。对于企业级的分布式应用程序而言,它更需要支持多种形式的用户接口。比如,网上商店需要一个HTML的界面来同网上的客户打交道,WML的界面可以提供给无线用户,管理者可能需要传统的基于Swing的应用程序来进行管理,而对对商业伙伴,基于XML的Web服务可能对他们更为方便。

MVC无疑是这样一个问题的有效的解决方法,通过在控制和显示逻辑分离出核心的数据存取功能,形成一个Model模块,能够让多种视图来共享这个Model。

在J2EE中有几个核心的技术,JSP,JavaBean,Servlet,EJB,SessionBean,EntityBean构成了J2EE构架的基石。JSP能够生成HTML,WML甚至XML,它对应于Web应用程序中的View部分。EJB作为数据库与应用程序的中介,提供了对数据的封装。一般EntityBean封装的是数据,SessionBean是封装的是对数据的操作。这两个部分合起来,对应于Web应用程序的Model部分。在技术上,JSP能够直接对EJB进行存取,但这并不是好办法,那样会混淆程序中的显示逻辑和控制逻辑,使得JSP的重用性能降低。这时候有两种解决方法,通过JavaBean或者Servlet作为中介的控制逻辑,对EJB所封装的数据进行存取。这时,JavaBean或者Servlet对应于Web引用程序中的Controller部分。两种类型的Controller各有其优缺点:JSP同Servlet的交互不容易规范化,使得交互的过程变得复杂,但是Servlet可以单独同用户交互,实际上JSP的运行时状态就是Servlet;而由于JavaBean的规范性,JSP同JavaBean的交互很容易,利用JavaBean的get/set方法,JSP不需要过多的语句就可以完成数据的存取,这能够让JSP最大限度的集中在其视图功能上,而且,在桌面应用程序中使用JavaBean也很容易,而用Servlet就相对麻烦许多。根据不同的问题背景,可以选取不同的Controller,有时候也可以两者混合使用,或者直接在Servlet中调用JavaBean。

在J2EE中,MVC是一个大的框架,这时我们往往把它不再看作为设计模式,而是作为体系结构模式的一个应用了。

总结
在这篇文章中,从设计的角度,对Java的类库进行了一些分析,并着重于设计模式在其中的使用问题。相信大家看了之后,不论对Java类库本身,还是设计模式,都应该有了一个更深层次的了解。当然,Java类库是一个非常庞大的东西,还有着许多设计精良的结构。因而,对Java源代码的研究,不论对于编码还是设计,都是很有裨益的。本人接触设计模式的时间并不很长,对其的理解可能会有一些偏差,如有谬误的地方,还请能够提出,大家能够共同的探讨。

需要说明的是,对模式的描叙实际上是有一套完整的规格(或者语言)来进行的,涉及到模式的意图(Intent),问题描叙(Problem),背景(Context),约束(Force),解决方案(Solution),结果(Resulting Context)等等。但这儿为了叙述的方便,并没有将它们一一列举。如果需要对模式有详细系统的研究,就应该对这些规格叙述有更深入的了解。

摘自IBM DeveloperWorks



版权所有:UML软件工程组织