2008年9月28日星期日

Java 设计架构(四)


第四节 数据持久层

数据持久化问题关键在于它的复杂性。 复杂性是应用开发过程中最令人头疼的一个问题。每当在一个应用中增加一个功能时,它的复杂性通常呈几何级的增长。这种复杂性往往导致程序的开发无法再继续下去。

专家将应用开发过程产生的复杂性分为两类,即非本质的(accidental) 和本质的(essential)。本质的复杂性是对于解决目标问题所必然产生的复杂性,非本质的复杂性是由于选择了不适当的开发工具和设计工具而产生的复杂性。对于一个功能确定的程序来讲,本质的复杂性是确定的,而非本质的复杂性则是没有限制的。因此,一个应用的开发要想较顺利地取得成功,就需要尽可能地减少非本质的复杂性。

设计模式使人们可以更加简单方便地复用成功的设计和体系结构。将已证实的技术表述成设计模式,也会使新系统开发者更加容易理解其设计思路。

    衡量一个系统优秀与否的关键因素,除了能够满足用户需求外还有如下方面:首先是灵活性。灵活性意指这种结构或模式不依赖于任何实际应用,应该与操作系统、应用程序无关。提供独立的结构,可以提供最大的重用。其次是可扩展性。随着业务的扩展,新的业务不断增加,业务逻辑自然增加,系统必然会进行修改或添加相应 功能模块。再次是可配置性。最后是安全性。

数据持久层的设计采纳了多种设计模式,最大限度的降低了系统内部各模块、子系统间的耦合性,使得系统相对易于扩展,并且能够在进行改变时,保证持久层的业务逻辑层相对稳定,基本不需要因持久层的调整改变而进行逻辑层的变动。

根据数据源不同,数据访问也不同。根据存储的类型(关 系数据库、面向对象数据库等)和供应商不同,持久性存储(比如数据库)的访问差别也很大。当业务组件或表示组件需要访问某数据源时,它们可以使用合适的 API来获得连接性,以及操作该数据源。但是在这些组件中包含连接性和数据访问代码会引入这些组件及数据源实现之间的紧密耦合。组件中这类代码依赖性使应用程序从某种数据源迁移到其它种类的数据源将变得非常麻烦和困难,当数据源变化时,组件也需要改变,以便于能够处理新类型的数据源。

数据持久层通过调整抽象工厂(Abstract Factory)模式和工厂方法(Factory Method) 模式,使DAO模式达到了很高的灵活度。 数据持久层使用数据访问对象来抽象和封装所有对数据源的访问。DAO管理着与数据源的连接以便于检索和存储数据,DAO实现了用来操作数据源的访问机制,内部封装了对 Hibernate数据操纵、事务处理、会话管理等API的封装。外界依赖于DAO的业务组件为其客户端使用DAO提供了更简单的接口,DAO完全向 客户端隐藏了数据源实现细节。由于当低层数据源实现变化时,DAO向客户端提供的接口不会变化,采用该设计模式允许DAO调整到不同的存储模式,而不会影 响其客户端或业务组件,即使将来不再采用Hibernate作为关系映射框架,上层客户端也不会受到任何影响。另外,DAO还充当组件和数据源之间的适配 器的角色。

当底层存储随着实现的变化而变化时,该策略可以通过使用抽象工厂模式实现。抽象工厂可以基于工厂方法实现而创建,并可使用工厂方法实现。该策略提供一个DAO的抽象工厂对象,其中该对象可以构造多种类型的具体的DAO工厂,每个工厂支持一种不同类型的持久性存储实现。一旦你获取某特定实现的具体DAO工厂,可以使用它来生成该实现中所支持和实现的DAO。

第五节 数据库连接池、缓存及系统性能的提升

通过使用框架和设计模式对数据访问的封装,极大的提高了软件的可扩展可维护性,同时也带来了系统的性能问题,J2EE系统通过以下一些技术解决这些问题。

  缓存(Cache),对于数据库来说,厂商的做法往往是在内存中开辟相应的区域来存储可能被多次存取的数据和可能被多次执行的语句,以使这些数据在下次被访问时不必再次提交对DBMS的请求和那些语句在下次执行时不必再次编译。同样,数据持久层采用缓存技术来保存已经从数据库中检索出来的部分常用数据。客户端访问持久层时,持久层将首先访问缓存,如果能够命中则直接从缓存中提取数据,否则再向数据库发送提取数据的指令。这种设计能够大幅度地提高数据访问速度。

  数据库连接池(Connection Pool),池是一个很普遍的概念,和缓冲存储有机制相近的地方,都是缩减了访问的环节,但它更注重于资源的共享。对于访问数据库来说,建立连接的代价比较昂贵,因此,数据持久层建立了"连接池"以提高访问的性能。数据持久层把连接当作对象,整个系统启动后,连接池首先建立若干连接,访问本来需要与数据库连接的区域,都改为和池相连,池临时分配连接供访问使用,结果返回后,访问将连接交还。这种设计消除了JDBC与数据源建立连接的延时,同时在应用级提供了对数据源的并发访问。

享元模式(Flyweight),面向对象语言的原则就是一切都是对象,但是如果真正使用起来,有时对象数可能显得很庞大,比如,数据库中的记录,如果以每条记录作为一个对象,提取几千条记录,对象数就是几千,这无疑相当耗费内存。数据持久层依据享元模式设计了若干元类,封装可以被共享的类。这种设计策略显著降低了系统的内存消耗。

第六节 Java反射技术

在不了解的情况下要想操纵一个Java对象,需要使用Java的反射技术。反射技术的出现为Java开发工具和服务容器项目的开发提供了新的技术手段,例如第四章中提到的Hibernate在运行时使用cglib类利用Java反射技术动态地将POJO转化为PO。

    Java的J2SE开发包中包含一个特殊的部分:Java反射接口,被放置在java.lang.reflect包中。反射使Java的类,包和接口实现了自我描述和动态操作的功能,这些功能对于Java应用程序的开发者来说,似乎过于基础化了,但是反射提供的不仅仅是是一项功能那么简单,通过反射,Java提供了新的一种非直接参与调用的方式,打破了一些固有的限制。反射已经为多个成功的项目所运用,产生了出乎意料的成功效果。配合一些把基础的反射功能扩展的工具项目,Java的动态功能得以出色地发挥出来。Hibernate也得益于反射,反射使Hibernate的代码更简洁有效,在操作时增强了功能。

反射技术机制使很多原来困难的任务变得容易实现了,反射技术的功能很强大,但是开发者在实际的开发过程中,并不一定非要使用反射技术不可,需要根据具体的情形来选择技术方案。即使一定要使用反射技术,也需要一些已有的工具的支持。

第五章 J2EE架构中各层的数据表示

第一节MVC三层体系结构中的数据表示要求

三层体系结构中的数据表示首先要做到的是数据的低耦合度,以Struts三层体系结构为例,三层数据的表示应该如下:

  • Web层的数据表示是ActionFormBean,数据来源于html Form post
  • 业务层的数据表示是VO
  • 持久层的数据表示是PO,其数据来源于数据库,持久层的数据表示例如CMP
按照MVC的设计原则,层与层之间应该保持相对独立,数据的传递在不同的层之间通常利用Java Bean来创建数据传输对象(Data Transfer Object,简称DTO),从技术的角度上面来说,采用DTO来传输数据可以减少传输数据的冗余,提高传输效率,更重要的是实现了各个层之间的独立,使每个层分工明确。模型层负责业务逻辑,视图层负责向用户展示模型状态。采用DTO,模型层对视图层屏蔽了业务逻辑细节,向视图层提供可以直接显示给用户的数据。在一个规范的J2EE架构中,不同层的数据表示应该被限制在层内,而不应该扩散到其它层,这样可以降低层间的耦合性,提高J2EE架构整体的可维护性和可扩展性。比如说Web层的逻辑进行了修改,那么只需要修改Web层的Form Bean结构,而不需要触动业务层和持久层的代码修改。同样的,当数据库表进行了小的调整,那么也只需要修改持久层数据表示,而不需要触动业务层代码和Web层代码。

先来谈谈ActionFormBean和持久层的PO之间的重大区别:在简单的应用中,ActionFormBean和PO几乎是没有区别,所以很多人干脆就是用ActionFormBean来充当PO,于是 ActionFormBean从JSP页面到Servlet控制层再到业务层,然后穿过持久层,最后一直映射到数据库表。系统以后需要修改其工作量无法想像。但是在复杂的应用中,ActionFormBean和PO是分离的,它们也不可能一样。ActionFormBean是和网页里面的Form表单一一对应的,Form里面有什么元素,Bean里面就有什么属性。而PO和数据库表对应,因此如果数据库表不修改,那么PO也不会修改,如果页面的流程和数据库表字段对应关系不一致,那么又如何能够使用ActionFormBean来取代PO呢?例如一个用户注册页面要求注册用户的基本信息,因此HTML Form里面包含了基本信息属性,于是你需要一个ActionFormBean来一一对应,每个Bean属性对应一个文本框或者选择框等。而用户这个持久对象的属性和ActionFormBean有什么不同呢?它会有一些ActionFormBean所没有的集合属性,如用户的权限属性,用户的组属性,用户的帖子等等。另外还有可能的是在ActionFormBean里面有3个属性,分别是用户的First Name, Middle Name, Last Name,或者干脆在User这个持久对象中就是一个 Name 对象属性。假设注册页面原来只要提供First Name,那么ActionFormBean就这一个属性,后来要提供全名,要改ActionFormBean,加两个属性。但是这个时候PO是不应该修改,因为数据库没有改。

第二节 J2EE系统中各层数据表示的设计

在一个完整的N层J2EE系统中应该如何进行合理数据表示设计呢?Struts是这样做的:

JSP(View) ---> Action Form Bean (Module) ---> Action(Control)Action

Form Bean是Web层的数据表示,它和HTML页面Form对应,只要Web页面的操作流程发生改变,它就要相应的进行修改,它不应该也不能被传递到业务层和持久层,否则一旦页面修改,会一直牵连到业务层和持久层的大面积的代码进行修改,Action就是它的边界。

Action(Web Control) ---> Business Bean ---> DAO ---> ORM --->DB

PO则是业务层和持久层的数据表示,它在业务层和持久层之间进行流动,它不应该也不能被传递到Web层的View中去,而ActionServlet就是它的边界。

再来看看整个架构的数据流程:当用户通过浏览器访问网页,提交了一个页面。于是Action得到了这个Form Bean,它会把Form Bean属性读出来,然后构造一个PO对象,再调用业务层的Bean类,完成了注册操作,重定向到成功页面。而业务层Bean收到这个PO对象之后,调用DAO接口方法,进行持久对象的持久化操作。当用户查询某个会员的信息的时候,他用全名进行查询,于是Action得到一个UserNameFormBean包括了3个属性,分别是first name, middle name, last name,然后Action把UserNameFormBean的3个属性读出来,构造Name对象,再调用BO,把Name对象传递给BO,进行查询。BO取得Name(注意: Name对象只是User的一个属性)对象之后调用DAO接口,返回一个User的PO对象,注意这个User不同于在Web层使用的UserFormBean。然后BO把User对象返回给Action。Action得到User对象之后,把User的基本属性取出,构造UserFormBean,然后把UserFormBean request.setAttribute(…),然后重定向到查询结果页面。查询页面拿到request对象里面的ActionFormBean,自动调用tag显示。

第六章 设计模式的概念与几种常用的J2EE设计模式

第一节 设计模式的概念

程序设计是思维具体化的一种方式,是思考如何解决问题的过程,设计模式是在解決问题的过程中,一些良好思路的经验集成。最早提到设计模式,人们总会提到Gof的著作,它最早将经典的23种模式集合在一起说明,对后期学习程序设计,尤其是对从事物件导向程序的人们起了莫大的影响。对于Java设计师来说,源代码的汇聚按尺度来分,可以分为由Java语句组成的"代码模式",由Java类和对象组成的"设计模式",由大尺度的构件组成的"架构模式"。

Gof 提到的OOP中设计模式涵盖三大类。

(1) 创建Creational 模式

对象的产生需要消耗系统资源,所以如何有效率的产生、管理与操作对象,一直都是值得讨论的课题, Creational 模式即与对象的建立相关,在这个分类下的模式给出了一些指导原则及设计的方向。

l        简单工厂(Simple Factory) 模式

l        抽象工厂(Abstract Factory) 模式

l        建造(Builder) 模式

l        工厂方法(Factory Method) 模式

l        原始模型(Prototype) 模式

l        单例(Singleton) 模式

l        多例(Multition)模式

(2) 结构Structural 模式

如何设计对象之间的静态结构,如何完成物件之间的继承、实现与依赖关系,这关系到系统设计出来是否健壮(robust):是否易懂、易维护、易修改、耦合度低等。Structural 模式正如其名,其分类下的模式给出了在不同场合下所适用的各种对象关系结构。

l        缺省适配(Default Adapter) 模式

l        适配(Adapter)模式

l        桥梁(Bridge) 模式

l        合成(Composite) 模式

l        装饰(Decorator) 模式

l        门面(Façade) 模式

l        享元(Flyweight) 模式

l        代理(Proxy) 模式

(3) 行为Behavioral 模式

对象之间的合作行为构成了程序的最终行为,对象之间若有良好的行为互动,不仅使得程序执行时更有效率,更可以让对象的职责更为清晰、整个程序的动态结构(对象之间的互相调用)更有弹性。

l        责任链(Chain of Responsibility) 模式

l        命令(Command) 模式

l        解释器(Interpreter) 模式

l        迭代子(Iterator) 模式

l        调停者(Mediator) 模式

l        备忘录(Memento) 模式

l        观察者(Observer) 模式

l        状态(State) 模式

l        策略(Strategy) 模式

l        模版方法(Template Method) 模式

l        访问者(Visitor) 模式

没有评论: