7、数据访问对象型式
几乎每个应用程序都使用某种形式的数据。这些数据可以存储在各种地方,如数据库、大型机系统、平面型文件或者其他外部服务。应用程序的业务及用户接口代码将需要一些数据访问以便为用户执行特定的任务。对数据执行的处理任务通常独立于该数据具体存储的形式。在业务和显示逻辑中包含专门的数据访问代码会把这些代码捆绑在某个特定的数据源上。
这样也就减少了解决方案的灵活性,因为每当该数据源发生变化时,这些代码都需要改变。
基层数据源的改变可能包括数据库的变化(例如从SQL Server转移到Oracle)或者整体模型的改变,如从基于JDBC的数据访问变化为通过实体EJB进行访问。
解决的办法是提供一个数据访问对象(Data Access Object,DAO)以抽象化对数据的访问。DAO封装了定位和访问数据源所需的代码。所有的业务或者表示逻辑都将使用DAO来检索和存储数据,如图9所示。
图9
DAO的接口定义了业务或表示逻辑之间的关系和数据。如果用于访问数据的基层定位和存储机制改变了,那么可以生成一个新的DAO来访问它。由于这个DAO具有固定的客户接口,它将不要求业务或者表示逻辑有任何变化,如图10所示。
图10
DAO的实际实现将根据不同的上下文环境而变化。如果客户是一个JSP,则DAO可以是一个简单的Java类、一个servlet或者一个EJB。如果客户本身是一个EJB,那么DAO很可能是一个简单的Java类或者可能是另一个EJB。
8、视图帮助器型式
表示机制和业务逻辑之间的不良划分造就的是不灵活的系统,同时增加了维护开支。使用JSP网页作为基于Web的用户接口是一个主要示例,因为很容易在JSP中包含过多的Java代码。如果这些代码采取的是业务逻辑的形式,那么这样会增加表示逻辑和业务逻辑之间的耦合程度,这对于灵活性也是有损害的。不论是业务代码还是表示逻辑代码,在一个JSP中包含大量的Java代码都意味着无法由Web设计者来维护,也不适合他们的使用。它还可能造成许多JSP网页之间的代码重复。
使用了视图帮助器型式,Java代码被封装在帮助器类中,如JavaBean或者自定义的标志。视图把业务或者表示处理分配给帮助器对象。在使用JSP的情况下,这样会从JSP中删除Java代码,使Web设计者能够使用标准的或者自定义的JSP标志来与之交互。典型的交互顺序如图11所示。
图11
注意在这种情况下视图是如何实例化帮助器并且从中获取数据的。如果帮助器作为JavaBean实现,则一个JSP可以通过标准的useBean和getProperty标志来访问所需的功能。
在这种情况下,帮助器访问业务功能,但视图帮助器(View Helper)也可以用于封装复杂的表示处理或者临时状态信息。
9、分配器视图型式
这种型式提出了一种把前端控制器(Front Controller)和视图帮助器(View Helper)型式结合起来以生成相关问题的一个集成解决方案的办法。这些问题包括重复性的和散乱的业务逻辑、需要把工作流与表示逻辑分开以及需要采用一些公共的服务如验证等。
分配器视图(Dispatcher View)型式是一个前端控制器型式和多个视图及视图帮助器型式的结合。与工作者服务(Service
to Worker)型式不同,在分配器视图型式中前端控制器和分配器并不实例化视图帮助器并使用它们的数据来帮助确定目标视图。
这样对视图进行的任何选择都将根据用户请求中的信息来进行。当相应的请求被传播到相应视图时,适当的视图帮助器(View Helper)将会被实例化并且将访问此视图要求的数据。不同类之间的关系如图12所示。
图12
可见,在这个模型中所有的外部数据访问都只在相应的视图被访问的时候发生一次。
10、工作者服务型式
这种型式提出了一种把前端控制器型式和视图帮助器型式结合起来的办法,以便生成一种相关问题的综合解决方案。这些问题包括重复性的和散乱的业务逻辑、需要把工作流与表示逻辑分开以及需要采用一些公共的服务如验证等。
工作者服务(Service to Worker)型式建立在一个前端控制器型式(Front Controller)的基础上。前端控制器型式把数据访问分配到视图帮助器(View
Helper),这些视图将在访问之前被生成并填入内容。一个分配器构成了前端控制器的一部分,决定向客户显示哪个视图并且与视图帮助器一起传播相应的请求。它们之间不同类的关系如图13所示。
图13
这种型式和分配器视图型式之间的主要区别在于,工作者服务型式更适合于需要在前端进行更多处理的场合。前端控制器和分配器先与视图帮助器交互,然后再把请求转发到相应的视图。例如,在工作者服务形式中,检索的一些数据可能实际上控制着相应请求分配到的视图。
11、值列表处理器型式(逐页迭代器)
在访问大量的数据时,客户应该试一试避免重复性地发出网络请求依次访问每个数据对象。这种简单地封装数据的远程对象如实体EJB,其使用方式对于网络和服务器资源的使用是十分低效的。
值列表处理器(Value List Handler)一般是作为一个有状态会话bean实现的,它要求基层的数据源获取所需的数据。数据源可以是一个数据库、一组实体EJB或者任何其他封装为一个数据访问对象(Data
Access Object)的来源。数据将被缓冲存储,然后根据请求提供给客户。图14显示了一个Web客户、一个值列表处理器和一组基层实体EJB之间的关系。
图14
值列表处理器型式将生成值对象(Value Object)来表示基层的数据。这些值对象将接着被缓冲存储,并且根据请求提供给客户。客户可以控制一次通过迭代器接口返回的值对象数量。包含的类之间的关系如图15所示。
图15
图16所示为上述处理的顺序示意图。
图16
注意这个型式在Sun Java Center J2EE Patterns分类目录中称为Value List Handler(值列表处理器),但在Sun
J2EE Blueptints中称为Page-by-Page Iterator(逐页迭代器)。
12、快速道读取器型式
尽管实体EJB是并发数据访问及持久性的一种功能强大的机制,但它们对于列出大量的数据却显得相当笨重。例如列出一个分类目录中的数据,通常的要求是获得只读数据以便浏览。同时,基层数据经常会改变,并且系统也并不严格要求数据的任何快照(瞬态值)都必须反映出相关数据的绝对最新状态。通过EJB发现器方法访问这样的数据造成的结果会是生成了许多EJB来完成很少量的有用工作。这对于通常情况下对应用程序的处理速度有较高要求时,都会造成很大的负担。
解决的办法是避开与数据关联的实体EJB,并且通过一个数据访问对象(Data Access Object)更直接地访问数据。这样的DAO通常会封装数据库访问,因此数据可以有效地从数据库提出。这样既提高了性能,又不会影响应用程序的稳定性。
快速道读取器(Fast Lane Reader)型式可以作为一个DAO实现或者作为一个用作DAO的会话正面(Session Facade)的会话EJB实现。客户和快速道读取器之间的关系如图17所示。
图17
|