快捷搜索:

何种设计模式和构架才能开发出最好的企业程序

营业逻辑和数据库造访决策

这里有2种完全不合的措施来设计JAVA企业法度榜样,此中一种选择是采纳标准EJB2实现道路(approach)。我更乐意称这种措施为重量级实现道路,当你应用重量级实现道路时你必要用会话beans(session bean)和消息驱动 beans(message-driven bean)去实现营业逻辑。你也可以应用DAOs(data access object)或者实体bean去造访营业逻辑

别的一种选择是应用POJOs 和轻量级构架,这种要领我称为POJO实现道路。当应用POJOs实现道路时,你的营业逻辑完全由POJO来实现。你可以应用持久型构架又叫做工具/关系映射构架(a.k.a=also know as )例如Hibernate 或者 JDO来造访数据库,再用Spring AOP(面向层面编程)来供给企业办事,比如事务治理和安然。

EJB3因为交融了POJOs和其他一些轻量级观点,以是对两者(指轻量级和重考锻揪叮┑那?植皇呛芮宄?>俑隼?樱琍OJO中的实体bean既可以再EJB容器内运行,也可以再EJB容器外运行,然而POJOs中的会话bean和消息驱动bean仍旧有重量级的行径,由于他们只能在EJB容器内部运行。以是,显而易见的,EJB3既是重量级的又有POJO的特点。EJB3中的实体bean是轻量级实现道路中的一部分。

在开拓历程中,重要的是从各类各样的设计中选择到底采纳重量级实现道路照样采纳POJO实现道路。决策可以影响法度榜样的几个方面,包括营业逻辑布局和数据造访机制。为了赞助从两种实现道路中择其一,来看这张范例的企业利用法度榜样布局图,布局图在图示1中,而且在设计历程中就必须判断到底应用那种策略。

Figure 1. A typical application architecture and the key business logic and database access design decisions.

法度榜样由收集基础表示层、营业层、持久层组成。收集基础表示层认真HTTP哀乞降为一样平常的浏览器客户端、XML和其他的胖体客户端天生HTML,比如为Ajax基础客户端天生HTML.营业层被表示层调用,用来实现法度榜样营业逻辑。持久层被营业逻辑层用来造访外部数据源,比如数据库和其他法度榜样。

表示层的设计不在本篇文章评论争论之内,来看图表的其他部分,我们必要抉择营业层布局的接口,这个接口是供给给表示层以及其他客户真个。而且还必要抉择如何造访能供多个法度榜样造访的数据库。我们还必须抉择若何处置惩罚短期事务处置惩罚事务和经久事务处置惩罚事务的并发问题。这些加起来一共有5种决策。每种决策都是要设计者来拟订,为了能看懂演示图(big picture)要求每个开拓者也都懂得这些策略。

这些决策直接抉择法度榜样营业和表示层设计的特征。当然,还要抉择一些其他很紧张的决策。比如营业处置惩罚(transactions)、安然问题、缓致意题以及若何整合法度榜样,然则关于这些问题平日在其他文献中评论争论在图表1中显示的五种决策,每种决策都有多种选择。每种选择根据它要办理的实际问题都有响应的优毛病。后续章节中,你会发明每种决策针对一个或多个领域时,在功能性、易开拓性、可掩护性和可用性方面有不合的平衡点。只管我是POJO实现道路的超级大年夜FANS,然则仍旧必要懂得其优毛病,以便于为你的法度榜样做最好的选择下面我们来懂得一下每种决策的大年夜纲和其选项。

决策1:组织营业逻辑

现在,很多的留意力都集中在某项技巧的优点和毛病,只管这很紧张,然则在本色上你必要懂得若何建构你的营业逻辑。假如不斟酌若何组织就去写代码是异常简单的。例如,为一个会话BEAN添加代码要比在域模式(domain model.: An object model of the domain that incorporates both behavior and data.)中判断应该添加那种新特点要简单的多。理论上你仍旧必要克意的为你的软件设计最相宜的营业逻辑。终究我信托你有过改动别人垃圾布局代码的惨痛履历

关键的决策是:到底应该用面向工具的实现道路照样面向历程的实现道路来实现你的法度榜样。这个不是关于技巧的决策,然则你技巧上的决策可以潜在的约束你的营业逻辑的组织布局。采纳EJB2技巧,有利于面向历程设计,然而POJOs和轻量级构架可以让你为特殊的法度榜样选择最好的实现道路

采纳历程式设计

虽然我是一个面向工具实现道路(指前文的应用POJO和LIGHTFRAMEWORK)的倡导者,然则有些环境下面向工具实现道路有些大年夜材小用,比如你只想实现一个异常简单的营业逻辑。而且,无意偶尔候,面向工具实现道路不太可行-—比如,你没有持久层构架来将你的工具映射到数据库中,在这种环境下,更好的措施是编写面向历程的代码,而且采纳Martin Fowler称作事务脚本(Transaction Script)的设计模式,要比采纳面向工具实现道路设计要好,由于你只必要写一个措施来调用事务处置惩罚脚本去处置惩罚表示层的哀求。

采种这种实现道路的一个很紧张的特征是,用于实现某种行径的类和数据存储区是分开的。在EJB2的利用法度榜样中,这种要领的营业逻辑和图表2中的设计是异常相似的。这种设计的核心全都集中在EJB或者POJO的行径上,由于他们实现了事务脚本,并且还操作那些 “哑”工具数据(由于他们只拥有很少的行径,大年夜部分都是数据)。由于大年夜部分的行径都集中在少量的大年夜型类上,以是代码会变的很难理解与掩护。

Figure 2. The structure of a procedural design: large transaction script classes and many small data objects

这种设计具有高面向历程的特点,而且基础不寄托面向工具说话的特点。假如你曾经应用过C或者其他非面向工具说话的话,你应该用过这种设计模式。假如这种模式很得当你的设计的话,用这种模式设计也是一种不错的选择。

这种直不雅的历程式开拓道路,异常的诱人,由于你只必要写代码就好了,不用斟酌若何组织你的类文件。但问题是,假如你的营业逻辑异常的繁杂,那么你的代码会变的恶梦般的难以掩护。以是,除非你要写的法度榜样异常的简单,否则你应该用面向工具设计你的法度榜样,而不要受面向历程的代码的诱惑。

采纳面向工具设计

在面向工具设计中,营业逻辑是由工具模型构成的,工具模型是由许多小类组成的关系网。这些类直接表现的是问题域的办理措施,如图3所示,在这种模式中,有些类只稀有据,有些类只有行径,然则大年夜多半的则两者都有,这是优秀的类设计的一种特征。

Figure 3. The structure of a domain model: small classes that have state and behavior

面向工具设计有许多的好处,包括可以前进可掩护性和可延展性。你可以用EJB2的实体bean来实现一个简单的工具模型。然则假如像要得到更多的好处的话,必须要应用POJOs技巧和轻量级持久层构架——比如Hibernate和JDO技巧。POJO可以让你开拓富厚的模型,这些模型可以拥有承袭和回调等特征。而轻量级持久层构架可以让你很简单的从工具模型映射到数据库。

工具模型的别的一个名字是域模型,Fowler称这种由面向工具道路来开拓的营业逻辑叫做域模型设计模式。(便是类的设计是直接用来办理问题的,则这种设计模式叫做域模型设计模式)

表模型设计模式

我曾经不停用域模型和事务处置惩罚脚本模型设计利用法度榜样。然则有一次我据说JAVA企业利用法度榜样可以用第三种道路来实现,这种道路便是Fowler所说的表模型设计模式。这种模式比事务处置惩罚脚本模式加倍的布局化,由于它为数据库中的每个表都写了一个类,而这个类中实现了所有对这个表的操作代码,这个类便是表模型类。(我的解释便是为每个表专门写个类,对表的所有操作,全都由这个类中的措施实现,相称于用一个类模拟的数据库中的表)。和事务处置惩罚脚本模式比拟,它将数据和行径分手封装到了不合的类中,由于表模型类的实例相称于真实数据库中的数据,这当然要比零丁的一笔记录要好的多。着末,可掩护性成了问题,然而表模型设计模式照样有一些好处的。

决策2:封装营业逻辑

前面几章,我没有说起若何组织营业逻辑。你必须抉择营业逻辑有什么样的接口。营业逻辑的接口由一些数据和措施组成,这些数据和措施由表示层来调用。在设计接口时重点必要斟酌的是:应该封装哪些营业逻辑的操作,而哪些操作不应该显示给表示层。封装接口可以前进法度榜样的可掩护性,由于经由过程暗藏营业逻辑的操作细节,可以实现改动营业逻辑而不影响表示层。毛病是,你必须为封装营业逻辑而特意的写很多的代码。

你还必要斟酌其他紧张的问题,比如若何处置惩罚事务处置惩罚,安然,和远程调用问题。平日这些也是营业逻辑接口要认真的问题。为了包管数据的连贯性,营业层的接口必须包管每个事务处置惩罚中的调用都能履行。同样,也要验证调用者是否有权限调用营业措施。营业层接口还要认真处置惩罚一些远程客户真个问题。

来斟酌一下选项。

EJB session faç;ade

经典的J2EE办理规划是:用EJB来封装营业逻辑-基础的session facade.EJB容器供给事务处置惩罚治理,安然,散播式事务处置惩罚和远程造访。Facade要领可以经由过程封装营业逻辑来前进法度榜样可掩护性。粗拙型(Coarse-grained) API经由过程削减表示层对营业层的造访次数,而前进机能(由于它将对各个营业流程的处置惩罚再封装了一次,以是对底层的营业流程来说,它的API是对照粗拙的,这里大概翻译的不好。请大年夜家见谅)。由于削减调用的次数,可以削减对数据库事务处置惩罚的次数,还可以前进工具在缓冲区的时机。假如表示层经由过程远程造访营业层,则这种API还可以削减收集包袱。图表4给出了一个EJB-based session facade的例子。

Figure 4. Encapsulating the business logic with an EJB session faç;ade

在这种设计模式中,表示层大概是经由过程远程来调用facade(相称于session的一个高档接口),EJB容器从facade中获得这个调用,并验证调用者的权限,然后开始一个营业处置惩罚。这个时刻facade调用底层的营业工具,而这些营业工具认真实现详细的营业逻辑。等Facade返回后,EJB容器提交营业处置惩罚或者让该营业处置惩罚轮回等待。

不幸的是,应用EJB session facade有一些严重的毛病。比如,EJB的会话bean只能在EJB容器中运行,这样就托慢了开拓和测试周期。别的,假如用EJB2,则用来向表示层传输数据的数据传输工具的开拓和掩护就会变的很逝世板而且空费时日。

POJO facade

对付许多法度榜样来说,更好的实现道路是用POJO facade和AOP协作。比如认真治理事务处置惩罚、表示层的连接和安然问题的Spring 构架。POJO facade对营业层的封假装风和EJB facade很相似,平日也可以用一样的公共措施。而POJO和EJB关键差别是用POJO代替了EJB,用AOP供给的办事(例如营业处置惩罚治理和安然机制)替代了EJB容器。表5中,显示了用POJO facade的例子。

Figure 5. Encapsulating the business logic with a POJO faç;ade

表示层调用POJO facade, POJO facade 调用营业工具。和EJB容器截获EJB facade要领一样,AOP经由过程“拦截机”来截获POJO facade,并验证调用者的权限,然后开始提交营业处置惩罚或让该营业轮回等待。

经由过程在利用法度榜样办事器外部开拓和调试营业逻辑,对POJO facade的开拓可以变的很简单,同时还可以得到许多EJB中会话Bean的好处,比如声明事务处置惩罚和安然。关键是,你可以少写点代码。你可以避免写数据传输工具类,由于POJO facade可以将工具域直接反馈给表示层;你可以应用依附打针的要领来将利用法度榜样组装起来,而不用在为JNDI写查找代码了。

然而,有些时刻不能那用POJO facade,比如它不能介入到远程客户端建立的散播式事务处置惩罚。

裸露模型域模式

应用facade的一个毛病是你必须写额外的代码,而且认真将工具域返回给表示层的代码很轻易掉足。假如表示层设法调用某个工具,而营业层却没有供给该工具,也会增添runtime error呈现的时机。假如你用JDO , Hibernate或者EJB3,则可以避免这种问题,措施是:将模型域(session区域)裸露给表示层,再将响应的工具域(存储工具的区域)返回给表示层,根据表示层在工具域之间的操作关系,持久层来导入响应的工具。(也便是把session区域给表示层,然后阐发它必要的工具,再让持久层去加载这些工具)这便是所谓的lazy loading 技巧。图表6中显示了表示层自由的造访工具域的设计图。

Figure 6. Using an exposed domain model

在图表6的设计中,表示层不经由过程facade而直接调用域工具,Spring AOP仍旧供给办事,例如事务处置惩罚治理和安然。

用这种实现道路的一个紧张的好处是,营业层不必要知道哪些工具必要调用,也不用知道那些必要返回给表示层。只管这挺起来很简单,然则你会发明一些毛病。这会增添表示层的繁杂度,由于你必须处置惩罚对数据库的连接。而且在基于Web的利用法度榜样中,事务处置惩罚治理也要异常小心,由于在表示层将数据反馈给浏览器之前,事务处置惩罚的数据必须维持精确。

决策3:造访数据库

无论你如何对营业逻辑如何的组织和封装,终极你照样要从数据库中取数据出来。在经典的J2EE利用法度榜样中,你有2个选择:JDBC——这个必要很多的底层代码;或者实体Bean——这个用起来异常艰苦,而且缺少紧张特性。比拟来说,应用轻量级构架令人痛快的工作之一便是:你有一些新的而且更有力的措施去造访数据库,而且这种措施可以显明的削减造访数据库的代码。让咱们来进一步钻研

直接用JDBC会有什么问题

近来忽然呈现了工具/关系 映射构架(比如JDO和Hibernate) 和SQL映射构架(比如iBATIS)这些不是凭空呈现的。相反,他们是在JAVA 同盟在JDBC屡造挫折之后才呈现的。为了懂得新构架呈现的缘故原由,这里咱们回首一下直接应用JDBC会呈现的问题。在许多法度榜样中直接应用JDBC不是一个好的选择,主要有以下三个缘故原由:。开拓和掩护SQL异常的艰苦而且消费光阴——一些开拓者发明要写宏大年夜而且繁杂的SQL语句异常的艰苦。反应数据库变更的SQL语句会变得异常耗时。你必须小心的斟酌就义可掩护性是否值得。。用SQL会使移植性变的很差——由于必要数据库的特殊SQL语句。假如一个法度榜样和多个数据库有关系,那么你就要写多个版本的SQL语句,这使得可掩护性变变成恶梦。。直接写JDBC代码要会异常耗时,而且轻易掉足。你必须写很多的样板代码去得到连接,创建和初始化适当的声明,还要用正确的声明去清理连接。而且你还要写代码去将JAVA 工具映射到SQL声明。因为要无奈的去写,JDBC代码很轻易掉足。

假如你的法度榜样必须直接运行SQL语句的话,那前面两个问题是无法避免的。无意偶尔候为了得到好的机能,必须要全力的写SQL语句,包括供应商供给的那些特殊器械。因为许多营业上的缘故原由,持久层可能会孕育发生纷乱的SQL语句,为了防止这种环境,DBA可能要求你的法度榜样来完全节制SQL语句的履行。平日,团队买进的关系型数据库过于宏大年夜,以至于利用法度榜样事情时会呈现一些和数据库有关的啰唆事务。根据“iBATIS in Action”的作者说这里会有一种环境呈现:“数据库或者SQL语句本身存在的光阴比法度榜样代码存在的光阴还要长,或者同一段SQL语句或数据库有多个法度榜样的版本。有些环境下,法度榜样已经用别的一种说话重写了,然则SQL语句和数据库却没有太大年夜的改变。” 假如直接应用SQL弄的你筋疲力尽,那么很幸运,这里有一种直接履行SQL语句的构架,它可比用JDBC要轻易多了。当然了,这便是iBATIS.

应用iBATIS

我开拓过的所有企业JAVA利用法度榜样,都是直接履行SQL语句的。早期的法度榜样是履行特定的SQL语句的,后来是用持久层构架再用少量的SQL语句构成的。一开始我直接用JDBC来履行SQL语句,然则后来,我常常写一些小的构架去完成JDBC中那些对照无聊的部分。我也用过一段Spring的JDBC类,这些类撤除了JDBC中的许多样板代码。然则无论是我自己写的构架照样应用Spring的类,在Java类映射到SQL语句的时刻都邑存在问题,这便是我为什么那么痛快的加入iTATIS 那边的缘故原由了。

iBATIS 不仅将利用法度榜样完全的与“数据库连接”、详细的SQL语句阻遏开来,更实现了经由过程XML描述文档来将JavaBean 映射到SQL语句。它用Java bean 自察机制来将“道具bean(bean properties)”映射为响应的数据库语句占位符,而且它可以将ResultSet后的结果构造为bean.它还可以经由过程数据库天生主键,自动加载相关的工具、实现缓存和lazy loading.这样,iBATIS 就撤除了许多履行SQL语句带来的苦差。经由过程编辑XML描述文档和调用少量的iBATIS的API,代替了写大年夜量的JDBC底层代码。

应用持久层框架

当然,iBATIS不能实现高层开拓和掩护SQL语句,而且短缺可移植性。为了避免这类问题,你必要用到持久层框架。持久层框架可以将工具域映射到数据库中。它供给了创建,查找,删除工具的API函数。当法度榜样要节制工具时它可以自动的加载响应的工具,还可以在事务处置惩罚停止时自动更新数据库。持久层框架经由过程工具/关系映射机制可以自动的天生SQL语句,工具/关系映射机制用XML文档定义了如何将类映射为表,如何将数据映射为列(column)和关系是如何被映射为外键与连接表的。

在持久层构架上EJB也有它的短处:实体bean.EJB2的实体bean有很多的不够,而且开拓和测试它会变得异常的逝世板。着末,很少用EJB2的实体bean了。在EJB3中会阐明那些问题。

两种最有盛行的轻量级持久层构架是JDO和Hibernate,前者是Sun的标准框架,后者是开源工程。两种框架都可以为POJO类供给持久层事务处置惩罚。你可以用POJO类来开拓和测试你的营业逻辑,而不用担心持久层的问题,这个时刻它会将类映射到数据库中的schema.别的,他们两个都可以在办事器法度榜样外部或者内部,这样可以进一步低落开起事度。用Hibernate和JDO来进行开拓比用老的EJB2的实体bean要惬意的多。

除了要抉择如何造访数据库外,还要抉择若何处置惩罚数据库的并行处置惩罚问题。下面来看一下,为什么并行处置惩罚问题那么紧张,同时看一下可实现的选项

决策4:处置惩罚数据库事务处置惩罚的并行问题

差不多所有的企业利用法度榜样都必要多用户和多个后台进程并行的更新数据库。2个数据库 处置惩罚事务同时造访同时造访同一个数据是很正常的,然则这种环境很可能引起数据库中的数据不同等或者引起利用法度榜样的不正常。因为大年夜部分的利用法度榜样都必要处置惩罚多个处置惩罚事务并行造访同一个数据,则它可以影响到营业和持久层的设计。

无论你是应用EJB照样轻量级构架,你的法度榜样必须可以并行造访共享数据。EJB2要求应用供应商供给的特殊扩充接口来实现并行,然而与此不合的是,JDO和Hibernate可以直接支持大年夜部分并行机制。更紧张的是,应用JDO和Hibernate不仅只设置设置设备摆设摆设简单,而且只必要少量的代码就可以实现了。

在这样主要先容几种“并行更新数据库处置惩罚事务”的选项的概要,这些事务处置惩罚和用户的输入无关。下一章,我主要先容一下若何在利用法度榜样级长光阴的并行更新数据库处置惩罚事务,这种处置惩罚事务会与用户输入有关,而且是由一系列的数据库事务处置惩罚组成的。

自力数据库事务

无意偶尔候对共享数据的并行造访可以简单的寄托数据库本身来实现,数据库可以设置为履行伶仃的数据——这只是对数据库而言。假如你对这种观点不认识也不要担心,你只要记着:假如利用法度榜样应用完全的伶仃事务要领,那么同时履行2个事务的结果和一个接一个的履行是一样的。(也便是说,假如你用伶仃事务的要领来造访数据库的话,你同时履行2个事务,就会变成一个接一个的串行履行了。)

这种措施大概听起来异常的简单,但问题是这种处置惩罚要领无意偶尔候会低落机能,由于若何实现对事务的伶仃是由数据库来抉择的。为了这个缘故原由,许多利用法度榜样都避免应用它,而采纳optimistic或者pessimistic 所锁,这会鄙人面讲到。

开放式锁定

并行更新数据的一种道路是用开放式锁定。开放式锁定事情道理是经由过程利用法度榜样来反省数据是否被更新(被其他事务改动造成的)而实现的。一种更通俗的实现开放式锁定的措施是在每个表中添加一个“版本列”(version column),对每个表而言,法度榜样每次改变此中一行的时刻都邑更新这个“版本列”。每个UPDATE语句中的WHERE语句会根据上次查询的成武判断这个版本号是不是被变动了。在事务造访数据库中的数据时,法度榜样中可以用PreparedStatement.executeUpadte()这个函数的返回值来反省行的个数,从而判断是否要继承履行UPDATE语句。假如数据中的行已经被其他的事务更新或者删除了,那么法度榜样会让该事务重新造访数据库。

用开放式锁定机制来锁定那些直接履行SQL语句的利用法度榜样是异常简单的。然则,用持久层构架(比如JDO和Hibernate)实现更轻易,由于他们已经供给了开放式锁定机制——在设置设置设备摆设摆设选项中。一旦在设置设置设备摆设摆设选项中,选中了这种要领,持久层构架会自动的天生SQL的UPDATE语句来完成版本反省的义务。开放式锁定的名字滥觞于一种假设的环境,在这种环境下:并发更新的时机异常少,而且法度榜样只能检测、覆盖这些数据而不能防止这种工作的发生。别的一种可选的道路是用守旧式锁定,应用他的假设前提是:并发更新肯定会发生,而且必须被禁止。

守旧式锁定

对付开放式锁定来说,别的一种道路是应用守旧式锁定。当一个事务读取某些行的数据时,他会对这些数据加锁,这样就防止其他的事务造访这些数据了。详细的实现是必要数据库支持的,然而不幸的是,不是所有的数据库都支持守旧式锁定。假如你的数据库支持话,那么你的利用法度榜样直接履行SQL语句来实现守旧式锁定将异常轻易。然则,可能你已经猜到了,在法度榜样顶用JDO或者Hibernate来实现守旧式锁定更轻易,JDO以设置设置设备摆设摆设选项的要领供给了守旧式锁定,而Hibernage供给了简单的API实现锁定工具。

除了可以处置惩罚单个数据库事务并行问题,经常你还必要处置惩罚多半据库事务的并行问题。

决策5:在长事务下处置惩罚并发造访

自力事务、开放式锁定、和守旧式锁定只能用在单个数据库事务上的,然而,许多的法度榜样必要 长光阴的 在多个数据库事务之间 读取或者更新 共享数据。比如,有一种环境描述的是 如何实现 用户编辑敕令,这和很多的进程有关,这些进程可能会运行 很长的光阴,而且它由 多个数据库事务组成。由于数据可能会被 一个数据库事务 读取,而又被 别的一个数据库事务 改动了,那么法度榜样必须对 共享数据的并发造访 进行不合的处置惩罚。这样就必须应用 开放式锁定设计模式或 者守旧是锁定设计模式,关于这两种模式会在Fowler的 Patterns of Enterprise Application Architecutre中具体先容。

开放式脱机锁定

模式一种选择是开放式锁定机制的扩展,它会从第一次读取数据开始,在编辑进程履行后反省数据是否已经被改动了。例如,你可以在数据库的共享数据的表中应用版本号来实现。在编辑进程开始的时刻,法度榜样将版本号存储到会话状态中,然后每次用户要存储数据时,利用法度榜样都要进行反省,包管数据库中的版本号和会话状态中的版本号同等。

由于开放式脱机锁定模式只有在用户进行保存改动过的数据时才可以检测,以是它只有在不成为客户的累赘的时刻,才可以很好的运行。但假如环境是:客户必须要撤销几个操作的话,那么就会由于这种锁定模式而异常忧?,那么更好的一种选择是用守旧式脱机锁定。

守旧式脱机锁定

模式在编辑进程开始时,守旧式脱机锁定要领经由过程锁定 共享数据,来办理 多个数据库事务 同时更新共享数据的问题,这样,这个编辑进程就可以防止 其他的用户来改动数据了。这种要领和守旧式锁定机制一开始描述的很类似,但它是靠法度榜样来实现的,而不是数据库。由于同一光阴内,只有有权利编辑共享数据的用户,才有权利去保存这些改动。

您可能还会对下面的文章感兴趣: