精华帖 (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接口对实体类的单向依赖关系,只有这样你的整个域模型才是稳定的。 |
|
返回顶楼 | |
发表时间: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中,还可能会带来很多潜在的问题,这就不说了。 |
|
返回顶楼 | |
发表时间: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 的实现的工厂的复杂性增加了 |
|
返回顶楼 | |
发表时间: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抛出的约束违例异常再转化为应用异常。 |
|
返回顶楼 | |
发表时间:2005-03-08
quote]2、Dao接口层设计变动会反过来导致实体类模型的变动,而实体类模型变动,又会影响其他Dao接口,这样会带来整个底层结构大面积变动。
当然我的假设是放在接口是稳定的这个假设之上的。 接口就是一个协议 是 domain object 和 ormaping (或者其他访问数据库的方案)的协议. 你的方案其实也是建立在 接口稳定的基础上,只不过我们分层的理念不一样 我市:view__>domain object -->ado (interface ,impl)-->database 你的:view———〉bo———〉ado———〉domain object __>database |
|
返回顶楼 | |
发表时间:2005-03-08
其实我的依赖也是单向的,domain object 和 daoimpl 都是单向依赖于 daointerface的
正如martin说的:rich domain object对于ORmapping tool的要求是很高的 |
|
返回顶楼 | |
发表时间: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方法里进行判定,抛异常。 |
|
返回顶楼 | |
发表时间: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、你的工厂必须同时在服务器端和客户端部署,并且这个工厂必须自己懂得判断什么是客户端环境,什么是服务器端环境,这又是一个过高的要求。 你自己可以比较一下我给出来的模型,就应该明白了哪种模型更加稳定,更加简单了。 |
|
返回顶楼 | |
发表时间:2005-03-08
frankensteinlin 写道 其实我的依赖也是单向的,domain object 和 daoimpl 都是单向依赖于 daointerface的
正如martin说的:rich domain object对于ORmapping tool的要求是很高的 你的依赖不是单向的,你的Dao接口也是依赖你的domain object的,不信你自己看看你的Dao 接口方法。这种双向互相依赖是个大忌。 |
|
返回顶楼 | |
发表时间:2005-03-08
其实前面写的代码1是
view-->bo-->dao-->domain object-->database 代码2是 view-->domain object -->dao-->database |
|
返回顶楼 | |