DAO 模式:讲解软件开发中的分层开发思想和技术,随着软件规模的扩大和业务的复杂,将一个软件分成多个层次进行开发,化大为小,分而治之,是缩短软件开发时间,提高软件开发效率的一种有效方法,也是目前软件开发一直使用的方法。
数据持久化:很多程序都有保存数据、读取数据的需要。程序运行时,保存在内存中的数据时瞬时的,关机之后将丢失。为了持久保存数据,需要将数据保存到磁盘中,比如保存到数据库或文件中。这样,程序启动时可以从磁盘读取数据,数据发生改变或程序关闭时,保存数据到磁盘,实现了数据的持久保存。
这里涉及到一个术语:持久化。持久化是将程序中的数据在瞬时状态和持久状态间转换的 机制。JDBC 就是一种持久化机制,将程序直接保存成文本文件也是持久化机制的一种实 现,但我们常用的是将数据保存到数据库中。
DAO,就是 Data Access Object(数据存取对象),位于业务逻辑和持久化数据之间 实现对持久化数据的访问。 在面向对象设计过程中,有一些“套路”用于解决特定问题,称为模式。DAO 模式提供了访问关系型数据库系统所需操作的接口,将数据访问和业务逻辑分离,对上层提供面向对 象的数据访问接口。
DAO 模式的好处就在于它实现了两次隔离。
隔离了数据访问代码和业务逻辑代码。业务逻辑代码直接调用 DAO 方法即可,完全感 觉不到数据库表的存在。分工明确,数据访问层代码变化不影响业务逻辑代码,这符 合单一职能原则,降低了耦合性,提高了可复用性。
隔离了不同数据库实现,采用面向接口编程,如果底层数据库变化,例如由 Oracle 变为 SQL Server,只要增加 DAO 接口的新实现类即可,原有 Oracle 实现不用修 改,这符合“开-闭”原则,降低了代码的耦合性,提高了代码扩展性和系统的可移 植性。
一个典型的 DAO 模式主要由以下几部分组成。
DAO 接口:把对数据库的所有操作定义成一个个抽象方法,可以提供多种实现。
DAO 实现类:针对不同数据库给出 DAO 接口定义方法的具体实现。
实体类:用于存放与传输对象数据。
数据库连接和关闭工具类:避免了数据库连接和关闭代码的重复使用,方便修改。
分层开发的优势
采用 DAO 模式后,数据访问代码被提取出来,由 DAO 接口和实现类来实现功能,形成了 数据访问层,该层代码一般放在 dao 包下,其他层不再考虑繁琐的数据访问操作,层层 之间通过实体类来传输数据。
每一层专注于自己功能的实现,便于提高质量。不同层次的关注点是不同的,数据访问层主要是数据库访问,业务逻辑层的重点是业务逻辑,前段表示层专注于页面的布 局和美观。根据不同层次需要由最适合的技术人员来实现,从而提高开发质量。
便于分工协作,从而提高效率。一旦定义好各个层次之间的接口,每个开发人员的任 务得到了确认,不同层次的开发人员就可以各司其职、齐头并进,进而大大加快开发进度。 便于代码复用。每个模块一旦定义好统一的对开接口,就可以被各个模块调用,而不 用对相同的功能进行重复开发。比如不同的业务逻辑模块如果需要对相同数据库表进 行操作,无需各自开发相应的 DAO 模块,复用即可。
便于程序扩展。比如 DAO 模式中采用面向接口编程,底层数据库发生变化,可以通过 增加接口的新实现来解决,实现无损替换,而不是修改原有代码,便于程序扩展。
分层的原则
每一层都要自己的职责。
上一层不用关心下一层的实现细节,上一层通过下一层提供的对外接口来使用其功 能。
上一层调用下一层的功能,下一层不能调用上一层功能。下一层为上一层提供服务, 而不使用上一层提供的服务。
与此类似,在分层开发中,分层也应检查类似原则。 封装性原则:简单而言,就是每个层次向外提供公开的统一接口,而隐藏内部的功能 实现细节,其他层次不能也没有必要了解其内部细节。
顺序访问原则:下一层为上一层提供服务,而不使用上层提供的服务。业务逻辑层可 以访问数据访问层的功能,而数据访问层不能访问业务逻辑层功能。不是相互访问, 而是顺序访问。 每一层都有自己的职责。对于数据访问层而言,它的职责就是执行数据访问操作。为了提 高封装性,应注意提供给业务逻辑层的接口不应该包含数据访问细节。
使用实体类传递数据
在分层结构中,不同层之间通过实体类来传输数据。把相关信息使用实体类封装后,在程序中把实体类作为方法的输入参数或返回结果, 实现数据传递,非常方便。关于实体类,主要有以下特征。
实体类的属性一般使用 private 修饰。
根据业务需要和封装性要求对实体类的属性提供 getter/setter 方法,负责属性的 读取和赋值,一般使用 public 修饰。
对实体类提供无参构造方法,根据业务需要提供相应的有参构造方法。
实体类最好实现 java.io.Serializable,支持序列化机制,可以将该对象转换成 字节序列而保存在磁盘上或在网络上传输。
如果实体类实现了 java.io.Serializable,就应该定义属性 serialVersionUID,解决不同版本之间的序列化问题。例如: private static final long serialVersionUID=2070056025956126480L;