论坛首页 Java企业应用论坛

读hibernate in action 和 PEAA 后发现感到的迷惑

浏览 18693 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2005-03-08  
frankensteinlin 写道
我始终认为rich domain object 的最大的优势就是应该自由在对象间导航,让他的客户端,不考虑对象是不是从数据库得到!order 对象里面 肯定有个集合时 orderitem 的集合,用户在拿到order后拿到他的orderitems 很自然,并不需要知道他是否被 orderADO调用过,对吧?难道每次先:
if Order.getItems()!= null orderADO.getItems(order)? 这样似乎很不透明阿


自由导航是根本不现实的。

抛开OpenSessionInView不谈(在这种模型下,domain object没有区别),考察分布式应用的场景,我的设计是这样的:

Order, OrderItem这是两个域对象,由于分布式应用场景,并且Order和OrderItem是的父子关系是lazy的,也就是说取得Order的时候不会连带取出orderItems,那么如果当你想取得orderItems,在本地必须通过一个Dao调用才能获得,即:

orderItems = orderDao.getOrderItems(order);;


注意,如果一个远程客户端,他首先获得了order,然后他想再获得items,他怎么办呢?我们可以把orderDao这个接口发布为一个远程服务(例如使用Hessian),那么客户端就可以调用了。

orderDao = ......  // 获取远程服务
orderItems = orderDao.getOrderItems(order);;


但是看看你的方案,你的方案是把getOrderItems方法封装给order对象的:

public class Order {
......
    public List getOrderItems(); {
          return orderDao.getOrderItems(order);;
    }
}


这在本机运行看起来似乎没有什么问题(本机运行可以使用OpenSessionInView,也完全不需要这样编程),但是放在远程环境中,一个客户端他拿到你这个order以后,他根本没有办法再通过order.getOrderItems()来获取items了。

我强调的原则其实很简单,就是要保证Dao接口对实体类的单向依赖关系,只有这样你的整个域模型才是稳定的。
0 请登录后投票
   发表时间:2005-03-08  
frankensteinlin 写道
if Order.getItems();!= null orderADO.getItems(order);?


这样肯定不透明吧 为什么?
因为 order 无法确定自己的成员变量的状态!哪怕它是private的(orderItems),
我认为我上传得martin的paee中的图很清楚了:
domain object 保持对 dao interface 的引用
dao interface 是实现可以另外考虑


我什么时候说过要这样写的? 看来你还没有搞明白我的意思。

一个系统的整个分层关系应该是层层向下依赖,越是底层的对象应该越稳定,越不应该变动,并且底层对象向上层对象的反向依赖是一个很大的忌讳。

在我的设计中,处在最底层的实体类模型,它绝对不会得知系统任何其他层的对象,它只表达自身的属性和实体类之间的关系。只有这样你才能够保证实体类模型的稳定。

实体类上层是Dao接口层,Dao保持对实体类模型的单向依赖。

一个系统是否采用OpenSessionInView,会影响到Dao和实体类的模型。如果采用OpenSessionInView,那么lazy的问题就是透明的,无需单独考虑的。

如果不采用OpenSessionInView,那么延迟的数据加载你必然要选择一个地方来实现,要么你选择在Dao接口上面增加方法来实现(这就是我的方案),要么你选择在实体类上面增加对Dao接口的反向依赖调用来实现(这就是你的方案)。

似乎看起来你的方案更酷一些,因为不管系统采用什么模型,你都是可以order.getOrderItems直接取到,但是问题并不是那么简单的:

1、这种方式在远程访问的情况下就会失效
2、Dao接口层设计变动会反过来导致实体类模型的变动,而实体类模型变动,又会影响其他Dao接口,这样会带来整个底层结构大面积变动。
3、当你想关掉lazy的时候,你又不可避免的去改动你的实体类模型,而实体类模型变动又会影响Dao接口层。

这就是反向依赖带来的问题。在一个具体的ORM中,还可能会带来很多潜在的问题,这就不说了。
0 请登录后投票
   发表时间:2005-03-08  
orderDao = ......  // 获取远程服务 
orderItems = orderDao.getOrderItems(order);;



public class Order { 
...... 
    public List getOrderItems(); { 
          return orderDao.getOrderItems(order);; // 获取远程服务 的实现的dao
    } 
}


但是我这里orderDao是个接口阿, order 依赖的是orderDao的接口
同样可以通过注入或者工厂的方法拿到 .  “获取远程服务 的dao阿”
宾不影响domain object的稳定性。

和你的复杂性是一样的,就是选取 orderDao 的实现的工厂的复杂性增加了
0 请登录后投票
   发表时间:2005-03-08  
pig345 写道
robbin 写道


什么叫做业务限制?什么叫做name是否重复的判定?不知道你在说什么。

我给出来的代码就是你上面给出来的代码的修正。


pig345 写道

DomainObject: folder 1 ---- * file
(这里仅仅是用Folder和File做个比喻,因为是计算机上最常见的东西,容易理解)

业务限制: file.name 在 folder 中唯一(同一目录中不能包含相同名字的文件)。


robbin为什么不能仔细看一下帖子呢?我称之为业务限制也许不妥,如果有更好的说法,请指正。

另外,我上面代码2里实际的意思是要在setName和setFolder里面判定name是否重复,如果重复的话,就要抛相应异常。


判断name是否重复这种需求不应该在OrderItem实体类里面由你自己手工写代码来实现,这种工作实际上是ORM的工作,你不应该自己去做这种工作。如果你需要对name重复做特定的工作,可以在Dao接口上面定义相应的应用异常,捕获ORM抛出的约束违例异常再转化为应用异常。
0 请登录后投票
   发表时间:2005-03-08  
quote]2、Dao接口层设计变动会反过来导致实体类模型的变动,而实体类模型变动,又会影响其他Dao接口,这样会带来整个底层结构大面积变动。
当然我的假设是放在接口是稳定的这个假设之上的。
接口就是一个协议 是 domain object 和 ormaping  (或者其他访问数据库的方案)的协议.
你的方案其实也是建立在 接口稳定的基础上,只不过我们分层的理念不一样
我市:view__>domain object -->ado (interface ,impl)-->database
你的:view———〉bo———〉ado———〉domain object __>database
0 请登录后投票
   发表时间:2005-03-08  
其实我的依赖也是单向的,domain object 和 daoimpl 都是单向依赖于 daointerface的

正如martin说的:rich domain object对于ORmapping tool的要求是很高的
0 请登录后投票
   发表时间:2005-03-08  
robbin 写道

判断name是否重复这种需求不应该在File实体类里面由你自己手工写代码来实现,这种工作实际上是ORM的工作,你不应该自己去做这种工作。如果你需要对name重复做特定的工作,可以在Dao接口上面定义相应的应用异常,捕获ORM抛出的约束违例异常再转化为应用异常。


pig345 写道

DomainObject: folder 1 ---- * file
(这里仅仅是用Folder和File做个比喻,因为是计算机上最常见的东西,容易理解)
业务限制: file.name 在 folder 中唯一。

这条业务限制至少需要在3个地方进行控制:

1)建立file时。
2)修改file.name时。
3)在folder间移动file时。

方法1:如果将这些操作放在BO里,那么势必会有如下操作:
FileManager.createFile(folder, fileName);
FileManager.changeFileName(fileName);
FileManager.moveFile(folder);

-------这么做有个问题,如果File里面有许多这样的字段,并且都有不同的业务限制的话,那么需要在FileManager里面针对每一个这样的字段添加代理方法,如果这种东西很多的话,最后FileManager是不是就变成了File的马甲(包含业务规则的Delegate)???

方法2:如果将这些操作放在DomainObject里,会有如下操作:
folder.createFile(fileName)
{
new File(this, fileName);
};
file.setName(fileName);
file.setFolder(folder);

-------这样单纯从OO上看,是把File的业务规则放在了File里面,应该是比较清晰合理的。
但是实际中这样的话,DomainObject就不可避免的需要使用DAO啊。


我的意思是说同一个Folder下面的File不能有相同名字,不同Folder下的File是可以重名的。ORM/数据库约束 能进行这种判定么?如果要进一步使用数据库触发器的话,那就要损失ORM的跨数据库能力了吧。

我目前能想到的如果是代码2的情况下,就是fileDao提供find(folder, name),File里面setName/setFolder方法里进行判定,抛异常。
0 请登录后投票
   发表时间:2005-03-08  
frankensteinlin 写道
orderDao = ......  // 获取远程服务 
orderItems = orderDao.getOrderItems(order);;



public class Order { 
...... 
    public List getOrderItems(); { 
          return orderDao.getOrderItems(order);; // 获取远程服务 的实现的dao
    } 
}


但是我这里orderDao是个接口阿, order 依赖的是orderDao的接口
同样可以通过注入或者工厂的方法拿到 .  “获取远程服务 的dao阿”
宾不影响domain object的稳定性。

和你的复杂性是一样的,就是选取 orderDao 的实现的工厂的复杂性增加了


不影响??????

好吧,你通过IoC注入,那么你的Order对象样子如下:

public class Order{
    private OrderDao orderDao;
    public void setOrderDao(OrderDao orderDao); {
         this.orderDao = orderDao;
    }
    ......
}


这意味着什么呢?意味着你的客户端程序必须运行在一个IoC容器中,并且这个IoC容器一定要像Spring那样支持远程服务查找,容器一要查找服务,二要注入给你。一个仅仅表达实体的对象类,它竟然要如此高度依赖容器才能运行,看来你不但要把Order在服务器端配置Hibernate映射文件,还要把Order类当做一个Spring的Bean类在服务器端和客户端同时配置上才能工作啊!呵呵,这种设计我就什么都不说了。

好吧,假设你说我不要IoC,我就工厂方法,那么你的代码是这样的:

public class Order { 
...... 
    public List getOrderItems(); { 
          orderDao =  .....  // 获取远程服务 的实现的dao
          return orderDao.getOrderItems(order);;     } 
}

你上面的代码忽略了最重要的一步!就是远程服务的查看。你这个实体如果放在本地,而不是远程,那么你必须修改getOrderItems(),改成通过工厂获得本地Dao实现类。

当然咯,你还会说,那我写一个工厂类,这个工厂类封装Dao的获得,如果是本地运行,我从IoC获取,如果远程,我通过Hessian获取远程服务接口类。

不错你这样这样做,但是注意:

1、你的这个Order类是不稳定的,因为Order变得开始依赖工厂了,而你这个工厂又比较复杂,要根据不同的环境创建出来不同的Dao实现,所以你的工厂对环境有很强的依赖关系,这样你的Order又对环境有很强的依赖关系,造成的结果就是,你的应用在不同的环境部署的时候,表现出来不同的行为,这就是一个大忌。底层类对环境适配的不稳定就带来你整个应用程序结构的不稳定。

2、你的工厂必须同时在服务器端和客户端部署,并且这个工厂必须自己懂得判断什么是客户端环境,什么是服务器端环境,这又是一个过高的要求。

你自己可以比较一下我给出来的模型,就应该明白了哪种模型更加稳定,更加简单了。
0 请登录后投票
   发表时间:2005-03-08  
frankensteinlin 写道
其实我的依赖也是单向的,domain object 和 daoimpl 都是单向依赖于 daointerface的

正如martin说的:rich domain object对于ORmapping tool的要求是很高的


你的依赖不是单向的,你的Dao接口也是依赖你的domain object的,不信你自己看看你的Dao 接口方法。这种双向互相依赖是个大忌。
0 请登录后投票
   发表时间:2005-03-08  
其实前面写的代码1是
view-->bo-->dao-->domain object-->database

代码2是
view-->domain object -->dao-->database
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics