背景:
根据数据源的不同,访问数据的方法也会有所不同,访问持久化的数据源,比如数据库,也会由于其存储类型的不同(关系数据库,面向对象的数据库,简单文件储存,其他方式)和提供商自定义数据类型的不同而有很大的区别。
出现的问题:
一般来说,程序使用一些共享的分布式组件来表示持久化数据.比如实体BEAN. 当一个程序中实体BEAN以比较直接的方式访问持久化数据时大多会考虑采用BEAN管理持久化方式(BMP)说明白点,就是程序中的实体BEAN包含有直接访问持久化数据的代码.另外一种情况,程序可以采用容器管理持久化,你不需要写任何代码,而是让容器自己来处理数据持久化的具体细节.
对于不同类型的数据持久化仓库,差异甚至会更大. 访问机制,API,以及一些其他特性,会因为他们是关系型数据库,面向对象型数据库还是一般的文件而大相径庭.需要访问以前遗留下来的系统或者诸如大型主机,B2B这样的专业系统中数据库的应用程序,会经常使用到一些自己特有的API. 这些特有的数据源对应用程序的编写提出了很大的挑战,而且很有可能在编写的过程中造成程序代码和数据访问代码间产生相互依赖性.当商业组件诸如:实体BEAN,会话BEAN,以及servlets和JSP帮助对象这样的表示组件需要访问数据资源的时候,可以用标准的API来实现其数据库的连接和数据的具体操作.但是,如果把连接数据库和数据的操作代码和这些组件写在一起的话,会导致这些组件和数据库操作之间的耦合,这种耦合的存在,使得在应用程序中从当前数据源类型迁移到另一种数据源类型变的十分困难和乏味. 如果数据源改变了,那么你的组件也不得不改变来适应新的数据源.
必要性:
1 像 bean管理实体bean, 会话 bean, servlets, 以及其他一些像jsp帮手对象这样的组件,通常需要从持久化的数据库或者原先遗留下来的系统以及 B2B, LDAP这样的系统中提取或存储数据。
2 用于持久化储存的API因他们的提供商的不同而各自不同。还有一些的数据源也可能有自己的一些特有的API或者是一些非标准的API。众多类型的数据持久化系统和载体,比如: 关系型数据库,面向对象数据库管理系统,XML文档,简单文件等等,使得API各不相同和性能各异。我们缺乏一种统一的API来对以上的不同的系统或者文件载体进行操作。
3 组件通常使用特有的API从内部系统或者是遗留下来的系统来访问或是提取数据。
4 当某些特定的访问机制和API包含在这些组件中的时候,将直接影响他们的兼容性。
5 组件需要对现有的持久化储存系统或者数据源的实现足够透明,以便在向不同的产品,不同类型的储存系统,和不同类型数据源中进行迁移的时候,变的简单。
解决方案
使用数据访问对象来抽象和封装对数据源的所有访问。数据访问对象负责管理与数据源的连接,来获取和储存其中的数据。
数据访问对象实现与数据源相关的访问机制。 数据源可以是关系型数据库管理系统,可以是像B2B EXCHANGE这样的内部服务,可以是LDAP库,或者也可以是通过CORBA IIOP 或者是低层sockets来访问的商业服务. 依赖于DAO的商业组件只对他的客户端暴露一些非常简单的DAO外部接口. DAO将数据源的实现细节对客户端完全的隐藏了起来. 因为,暴露给客户端的DAO接口在低层数据源的实现发生改变时并不会随着改变,所以这种设计模式使得DAO可以适应不同的数据储存方式类型而不影响客户端和商业组件.最主要的, DAO还在组件和数据源之间扮演着协调者的角色.
以下是DAO设计模式中各个模块的解释:
1 BusinessObject指的是数据客户端,他通常需要去访问数据源以获得数据或储存数据.一个BusinessObject除了访问数据源的servlet或者helper bean之外也可以是会话BEAN,实体BEAN以及一些其他的JAVA对象.
2 DataAccessObject 是这个设计模式的核心部分, DataAccessObject为BusinessObject抽象了底层的数据访问实现的细节,使得访问数据变得透明. BusinessObject还将数据的装载和储存交给了DataAccessObject进行代理.
3 DataSource他表示的是数据源的实现. 一个数据源可以是像关系型数据库管理体统这样的数据库,可以是面向对象型的数据库管理系统,可以是XML文档,也可以是简单文件等等. 当然他也可以是其他的系统,(遗留系统,大型主机),可以是服务(B2B服务,信用卡局服务)或者是像LDAP这样的数据库等.
4 TransferObject他代表的是传递对象,一般用于数据的载体. DataAccessObject使用传递对象来将数据返回给客户端. DataAccessObject也可以使用传递对象来从客户端接受数据来将原先数据库中的数据进行更新.
策略:
由于每一个BusinessObject都有着相应的DAO,所以在BusinessObject,DAO,和底层实现之间是可以建立起确定的关系的(比如在关系型数据库中的表格)。一旦他们之间的关系建立了,我们就可以为这个应用使用专门定制出代码生成器生成涉及到该应用所有的DAO的代码。产生DAO的元数据还可以通过开发人员定制的描述符文件来获得.代码生成器也可以通过自动的对数据库进行检查,然后按照实际情况来提供一些必要的DAO来访问数据库. 如果对于DAO的要求过于复杂,则考虑使用第三方工具来为关系型数据库提供对象到关系的映射.这些工具一般都包含图形化的用户界面可以方便的将商业对象影射到持久化对象上,于是就可以定义DAO了。这些工具可以自动在影射一结束就自动的产生代码,不但如此,他门可以提供一些附加的好处,比如结果缓存,查询缓存,与应用服务器的整合,于第三方产品的整和(分布试缓存)等等.
1 工厂模式策略:
DAO设计模式可以通过采用抽象工厂和工厂方法模式来边的非常的灵活.
当底层数据储存实现不需要发生改变时,该策略可以使用工厂方法设计模式实现,来产生应用中所需的DAO.
当底层数据储存实现不得不发生变化的时候, 我们可以用抽象工厂模式来实现这个策略. 就象在设计模式:可重用面向对象软件的元素这本书中建议的那样,抽象工厂先创建然后再使用工厂方法的实现. 在当前情况,该策略可以提供一个抽象的DAO工厂对象(抽象工厂),用他来创建不同类型的具体DAO工厂.,每一个工厂都各自支持一种不同的数据持久化储存的实现. 一旦你为某个特定的实现获得了具体的DAO工厂,你则可以用这个工厂来产生那个特定实现所支持和实现的DAO对象.
优点与缺点:
DAO设计模式带来的好处.
1 透明化:
商业对象可以在完全不知道数据源如何具体实现的情况下来使用数据源. 访问数据源是透明的,因为实现细节已经被隐藏进了DAO.
2 迁移简单化:
DAO层的出现,使得应用程序向不同的数据库实现进行迁移变的容易.商业对象可以对底层数据实现一无所知.这样,迁移只涉及到了对DAO层的修改. 另外,如果使用工厂策略,则使为每一种底层数据实现提供一个具体的工厂实现成为可能.在这种情况下,迁移到一种不同的数据实现,其实就相当于为这个应用程序再提供一个新的工厂实现.
3 减少在商业对象中的编程难度.
由于DAO管理着所有的数据访问细节,因而大大简化了在商业对象和其他使用DAO的数据客户端里的代码.所有的实现细节相关的代码比如(SQL 语句)都包含在DAO而不在商业对象中. 这样使得代码变的更加健壮而且大大提高了开发效率.
4 将所有的数据访问都单独集中到一层中去.
因为所有的数据访问操作现在都已经被DAO所代理,所以这个单独的数据访问层可以被看作可以是将数据访问实现和其余应用程序相互隔离的一层. 这样的集中,使得应用程序可以更加容易的来维护和管理.
缺点:
5 对容器管理持久化无用
由于EJB容器使用CMP(容器管理持久化)来管理实体BEAN. 容器会自动的为持久化储存访问提供服务.应用程序使用容器管理的实体BEAN则不需要DAO层的参与.因为应用程序服务器本身就可以透明的提供这些功能.然而,DAO在组合式CMP和BMP需要的场合下还是有用的.
6 增加了多余的层.
由于DAO在数据客户端和数据源之外多创建了一层对象,因而,需要对他进行设计和实现,来均衡这个设计模式的利弊. 但是,一般来说,采用此设计模式还是利大于弊的.
7 需要对类的相互继承关系进行设计.
当使用工厂策略的时候,具体工厂类的继承关系和由这些工厂类生成的产品需要进行设计和实现. 我们需要仔细考虑这些多付出的工作是否真的可以产生出来更高的灵活性. 使用这个策略会使设计变的更加复杂,然而,你可以先从工厂方法模式开始来实现这个策略,然后在需要的情况下再转向抽象工厂