首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

深思DAO

2012-09-23 
反思DAO反思DAO1.DAO模式带来的困惑OO语言和关系型数据库在表达数据的方式上,存在着天然的阻抗性。所以才会

反思DAO
反思DAO
1.DAO模式带来的困惑
    OO语言和关系型数据库在表达数据的方式上,存在着天然的阻抗性。所以才会有DAO模式的出现。最为正统的DAO,包括接口、具体实现类,及工厂。然而现在,由于Spring 等IOC容器的出现,对正统的DAO模式有了很大的影响。工厂早已被Spring取代(其实两者都实现了我们使用工厂的目的),剩下的就只剩接口和实现类了。我们在学习正统的DAO模式的同时,又困惑于实际开发时,DAO模式早已大变身,早已和书中说的不一样了。我想,在现在这种MVC+DAO的开发范式中,DAO层,是一个让人非常纠结的层,很头疼。每个实体对象都需要一个DAO与之对应,同时每个DAO做的事情又都是如此的相似,无非就是些CRUD操作。有了Spring之后,我们做的事情就更少了,无非是把真正的操作委托给Spring的Template。于是很多人想到,可以定义一个共用的DAO,来完成所有的CRUD操作。我们会纠结在这两种方式中,有人觉得一个CommonDAO就够了,有的人觉得我们需要一个单独的一层来管理DAO。
    不知道大家是否和我一样,充满困惑, 心中有着许多的节,总是觉得有什么地方不妥,却也非常无奈!在此,我谈谈我的看法,希望和大家交流思想,希望得到更好的解答。
2.我们是否还需要为DAO定义一个接口? 
    DAO接口是否还有存在的意义呢,这个问题,我们还是先回到DAO模式本身来看吧。DAO模式的存在,是为了给应用程序的上层,提供一个面向对象的,稳定的接口,处理对象到关系型数据库的转换。有了这样一个稳定的接口的存在,使得我们在设计的时候,可以不用考虑关系型数据库的存在,而更加的关注于业务逻辑本身(很肉麻的一句话,又有几个人是这样做的)。这样的思想,和DDD中Repositories有异曲同工之妙。但是,要达到这种目的,并非就得定义一个接口层!DAO模式不是银弹,并不是所有的程序,都需要我们耗费巨大的精力去维护一个DAO层。在我们设计一个应用程序的时候,程序的复杂性是否需要我们使用DAO模式呢?不知道有多少架构师在设计时考虑过这些问题(虽然我还不是)。而采用Martin在书中描述的那样,将这些操作集成到实体对象中。或者,只是提供DAO的实现类,不需要接口,因为程序没有那么复杂,即使将来,程序扩展需要,再抽象出接口层出来,也不会是一件困难的事情。为什么,我们国内的项目,一上来就是MVC,一上来就是DAO模式呢?
    当然,有些时候,你不得不定义一个接口层。比如,你是发布一个产品,一个可以支持多个数据库的产品,或者,你的程序有多个数据源,这样,我们花如此大的精力,来维护一个DAO层的意义也就体现出来了。此时,你就会体会到封装和多态给我们带来的好处了!面向抽象编程的本质不就是封装一切可变的,使得我们依赖于一个相对不变的东西吗?
3.CommonDAO和DAO层
    当我们纠结于这个问题时,我们还是静下心来好好分析下,这两种方式有什么不同吧?
    1.相同点:两种方式,我们的目的都是可以达到的,完成需要的功能。
    2.如果使用CommonDAO,相对于传统的方式,会给我们的程序带来什么样的影响呢?增,删,改就没什么好说的了,最大的不同应该是查询。由于业务逻辑的复杂性,不同的实体,往往会有不同的查询要求。这样的话,如果我们真的想用一个DAO来搞定这些查询的话,就会遇到很大的挑战,这里我们有两种方式可以选择。一种是调用DAO的上层提供SQL,这种方式,可能是国内很多人采用的方式吧!这种方式从设计的角度来看,应该是一种糟糕的设计,他已经背离了我们使用DAO模式的初衷----隔离数据存储的细节。这种方式,会将关系型数据库所带来的复杂性蔓延到整个系统中,不但使得软件没有一个清晰地结构,而且依赖的关系发生了倒转,下层依赖上层,无形中增加了系统的复杂性。不管是需求变更,还是后期的维护,都会是一场噩梦。SQL本身的标准化,使得这种影响相对的减小了,但在我们实际开发过程中还有一个绕不过去的坎,在这里却是凸显了出来,那就是分页查询,每个数据库支持的方式都不一样!设想一下,如果开发的中途,客户说Oracle太贵了,买不起,我们要改用SQLServer,会发生什么样的事情?不管怎么说,编程从此不再快乐,这是肯定的!有过这种经历的人应该不少,反正我自己是深有体会!我们在使用Java这种面向对象的语言,切换数据库能带来那么大的影响?除非你的程序只是在用Java传递SQL,否则你应该自豪的告诉别人,我们用的是OO的语言,不就是换个数据库嘛,小意思(当然这些话,是不会对客户说的 )!如果客户最终要用MySQL怎么办?因为他听说MySQL不要钱啊,天啊,我要跳槽了,不如Kill Me吧!另一种方式就是不用SQL了,改用HQL,在SQL之上,抽象出来一个查询语言,由程序决定该怎么翻译。这种方式相对于前一种方式来讲,算的上是一大种进步(当然,不用你自己去翻译)。因为,采用这种面向对象的查询语言,不仅相对容易理解,而且,达到了我们一个目的,就是封装会变化的东西,使得我们依赖于可以看做不变的东西。这样,像切换数据库,或者支持多个数据库这样的事情,我们程序员都会乐呵呵的做这些事情,有了像Hibernate这样的工具,确实,使得我们的生产力得到了大大的提高。当然这种方式依然是不完美的,结构问题,依赖倒置的问题依然存在。好在,查询不会是业务领域的核心逻辑。目前为止,我们得出的结论应该是,要使用CommonDAO,就得使用HQL这种可以面向对象的查询语言。我们可以用现成的,也可以自己去创造,如何选择,我想不言而喻。使用第二种方式还有一个挑战,就是动态查询。要解决这个问题确实不是一件容易的事情。从目前我所了解的方式来看,一个是iBatis的Dynamic查询,另一种就是Hibernate的Criteria。第一种方式,需要一个Map参数。从OO的角度来看,这种变相的结果集方式,早已失去了Java这种强类型语言的类型检查的优势,不过确实是解决问题的一个一个好办法(虽然SQL还是得自己去拼)。这种处理方式早以赢取了许多程序员的芳心。让我们再来看看Hibernate的处理方式。Hibernate虽然考虑到了这一点,但是确有很多不如人意的地方。一来,Hibernate本身的实现就不够完美,另一方面,在使用Spring之后,会有些问题。因为要使用Criteria,必须要Hibernate原生的Session,但Spring给我们的确是他封装过的Session,这样你使用Criteria时,就会产生类型转换的问题,所以即使Hibernate本身,也不推荐我们使用Criteria。不知道老外是不是很少做这样的事情,反正国内的项目查询的条件总是一大堆,纠结的很,很多时候,会感觉整个系统除了查询没别的什么功能了。老外没搞好这事,不知道我们国人中有没有想做这方面贡献的,反正我翻遍JavaEye没发现。可能很多人心中有些想法,只是没时间实现吧。这玩意,我也想过,抖出来让大家看看。(不过已经有老外把这事给搞定了,Google的 Warp Framework,我知道这事后,很兴奋,跑网吧去想下源代码回来学习,可惜没找到)思路很简单,就是利用Annotation,实现这个动态查询。先说下为什么我觉得对Java来讲,这样的方式是最好的。这种方式离开了Hibernate不行,还要定义一堆的Query Object。所以我也纠结的很,不知道自己做这么到底意义有多大。代码也写了一半,我想大家照着这个思路下去,都可以做成。做Web开发,还有一个绕不过去的地方就是MVC,例如我们最常用的Struts1,必须为每个Form定义一个FormBean。现在有一种思路就是FormBean里面再定义一个对象VO,将Formbean的职责委托给VO。我希望的是,这个VO就是我们需要的Query Object,在每个属性上,加上对应的Annotation,在CommonDAO中,我们只需在通用的动态查询方法中解析VO里面的Annotation,然后拼成HQL,传递给Hibernate执行,返回结果就行。我想,我们应该可以做出一个可以满足大部分需求的HQL生成工具出来,也能让PG心里觉得我们的技术不是很落后,Annotation,jsut cool !当然这里你可以直接用FormBean这个VO,但我的固执狂的劲又上来了,下层的结构又依赖上层的结构了,还分层做什么?所以我选择这种方式。我只是提供一种思路 ,希望能有更好的方式!
    3.CommonDAO和传统的DAO还有一个很大的不同,这个问题就是责任泛化!我不知道责任泛化这个词是谁发明的,应该跟契约式设计有关系!当然,这不仅仅是一个设计的问题。是否,每个实体都需要更新功能?是否每个实体都可以删除?如果不是,但是你却给客户端程序那样做的能力!最小责任化,也即我们最常挂在嘴边的,编程,简单就是美!
    4.传统的DAO模式,接口加子类,最让现在的我们诟病的应该就是你要为每一个实体提供一个DAO。
4.总结
    还是那句话,没用银弹,没有完美的解决方式!每种技术本身都有其优缺点,没有最好,只有选择的问题!作为程序员,特别是对一个软件的架构有发言权的人,更应该保持一刻平静的心,在合适的时候,做合适的选择。

热点排行