`
jayluns
  • 浏览: 143959 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

如何在struts+spring+hibernate的框架下构建低耦合高内聚的软件(转)

 
阅读更多

问题的提出

分析与决策



1.       编写DAO的时候不要直接去使用hibernate或spring对hibernate的支持。
现在我们在编写 DAO的时候普遍都是直接继承 spring hibernate的封装类 HibernateDaoSupport,然后使用该类提供的诸如 等等。另外,在使用 方法实现一些更复杂的 hibernate功能的时候还会使用 hibernate的类,诸如 Query, Session, Type等。这样直接使用 spring hibernate的类存在的问题在于,你的代码将不得不依赖与 spring hibernate的某个版本。比如说,现在 hibernate3出来了,改动挺大,实际上最要命的是包结构, hibernate2的包结构是 ,然而 hibernate3 。同样, spring为了支持 hibernate3,包名也改为 。假如,你现在新开发一个项目,这没什么关系,如果是升级一个项目问题就来了。如果你希望将你的一个项目从 hibernate2升级为 hibernate3,你不得不修改 DAO中所有对 hibernate spring-hibernate的引用。如果你的代码中出现 hibernate2 hibernate3不兼容的方法和类,比如 (在 hibernate3中已经没有了) ,你还将不得不改写。那么你可能会说,我不会这样升级。如果你的软件生命周期有好多年, hibernate升级到 4,升级到 5,你还是依然使用 hibernate2?如果你以这种方式开发一个平台,你能要求所有使用你平台的软件项目都只能使用 hibernate2?更进一步说,我现在开发一个产品,今后的客户将是成千上万。经过 1 2年我需要升级了,这时我的升级包有几十 M,几乎把所有的 DAO都换了个遍,这样的升级无异于重装。也许,有人会提出另一个方案,在 HibernateDaoSupport DAO中间增加了一个基础类,这样将基础类中的 ,改为了 ,这样其下面继承的 DAO就不用改动了。然而在源码上是小小的改动,但对于类来说,两个不同版本的 其相关的属性和方法还是有不少变化,那么在基础类重新编译的同时,你的继承类重新编译否。既然已经重新编译了,因此你的所有 DAO在升级的时候依然要打入升级包,问题依然存在。
以上问题,究其原因,是我们项目中的 DAO依赖于 hibernate spring,因为我们对它们的使用是继承,是一种很强的关联,就是一种依赖。我们只需要稍微进行一些调整,就可以解决这个问题,那就是不使用直接继承,而使用接口进行分离。可以使用 Façade模式,先建立一个叫 BasicDao的基础类,从名称我们可以看出,它是所有 DAO的基础类,实现 DAO操作所需的所有诸如 等方法,除了一些基本的方法,诸如翻页查询、 getCount、解析查询条件形成 HQL语句等功能也在这里实现,但是不要使用与 hibernate spring有关的任何方法和类。同时, BasicDao调用一个叫 DaoSupport的接口, DaoSupport的接口则是提供持久化所需的基本方法,最原始的元素。然后,我为 DaoSupport接口提供各种不同的实现,比如 hibernate2的实现 DaoSupportHibernateImp hibernate3的实现 DaoSupportHibernate3Imp,整个结构如下图所示。 BasicDao可以使用 hibernate spring提供的方法,但是不是直接使用,而是通过调用 DaoSupport的实现类来使用。然而 BasicDao到底是使用的那个实现类,我们通过 spring IoC,通过配置文件来决定到底使用哪个实现。同时, BasicDao也不要使用诸如 SpringContext的类来实现 IoC,而是通过建立 方法,然后在 spring配置文件中建立引用。
2.       编写Action的时候不要直接使用spring和spring的继承类
前面我说了应当避免 DAO引用 spring hibernate及其继承类。同样的事情也发生在 Action中。由于 Action通常不纳入 spring的管理,因此 Action在通过 spring调用某个 BUS的时候,往往是去引用一个叫 的类( spring的类 的继承类 然后使用它的 方法。如此的使用,我们的 Action将依赖与 spring。我们同样可以使用一个叫 BasicAction的父类,然后用一个接口来隔离 spring。由于 Action通常不纳入 spring的管理,我们通过一个 的配置文件来决定接口到底调用哪个实现类。这样的结构的另一个好处是,我们还可以将所有 Action都必须使用的诸如写日志、用户校验、异常处理都放在父类 BasicAction中,提高系统的可维护性。
 
3.       当BUS需要获取别的模块的数据的时候,不要直接去使用该模块的DAO
我举一个简单的例子:我需要设计一个软件评审的管理软件,该软件分为评审组织者制订评 审计划、评审者分别填写评审表后由评审组织者汇总评审表、评审组织者制作评审报告。这是一个非常简单的项目,分成了三个人来完成。但是项目进行快结束的时 候却出现了问题。填写评审表需要获得评审计划中的一些数据,制作评审报告的数据来源于评审表。项目组在开始编程前先开了一次会,大家约定好了各个部分的数 据格式及其规则,然后开始工作。然而数天后项目组把各个模块整合以后发现,系统根本跑不起来,为什么呢?设计评审计划的人发现,所有评审计划应当按照产品 编号来进行管理而不是项目编号。由于这个变更,填写评审表模块在待评审列表中什么都无法显示;同样,设计评审表的人发现,在一个评审计划中评审表与评审者 不是一对多的关系,而是一对一的关系,因而修改了这两个表的关联。因为这样,在制作评审报告时就不能正确得到评审表数据。其实一个软件项目在整个进行过程 中总是不断变更。我们需要做的不是去抑制这些变更,而应当是通过软件的结构去适应这些变更,即是降低各模块间的依赖(耦合),提高内聚。
拿这个实例来说,当评审表需要调用评审计划的数据的时候,不应当是自己写一个 DAO去调用评审计划的数据,而应当是调用评审计划的接口,将这个任务交给评审计划类来完成。当评审报告需要调用评审表的数据的时候,同样应当去调用评审表的接口,由评审表来实现。同时,这种调用应当是去调用 BUS层的接口。为什么呢?比如在评审计划中的一个业务逻辑是只有在评审计划发布以后才能制作评审表,那么怎样才是已发布的评审计划呢?这个业务逻辑应当由谁来定义?当然是评审计划。在什么地方定义?当然是 BUS而不是 DAO,因为 DAO仅仅是实现数据的持久化,而 BUS才是实现业务逻辑的地方。既然如此,如果评审表去调用评审计划的 DAO, 那么已发布评审计划的业务逻辑必然包含在了评审表的业务逻辑里了。我们假设有一天,已发布评审计划的业务逻辑发生变更了(实际上这样的会在你毫不经意间就 发生了),编写评审计划的人会很快就修改了评审计划的业务实现并且测试通过了。他不知道评审表里也包含了这样的业务逻辑,因而修改后的程序在运行到评审表 的时候就很可能会出错。不幸的是,在实际工作中,同样一个业务逻辑可能包含在无数个你可能知道,但你也可能不知道的代码中。这样的结构就是一个不易于维护 的差的结构。
 
总结:从技术升级和需求变更两方面适应变化



分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics