2010-02-25 56 views
26

在我们的传统Java EE应用程序中,有大量值对象(VO)类,它们通常只包含getter和setter,可能是equals()hashCode()。这些(通常)是要保存在持久性存储中的实体。 (为了记录,我们的应用程序没有EJB - 虽然可能会在将来更改),并且我们使用Hibernate来维护我们的实体。)用于操作VOs中数据的所有业务逻辑都位于不同的类(不是EJB,只是POJO)。我的OO思想讨厌这个问题,因为我确实相信某个班的操作应该在同一个班上。所以我有一种冲动,想要将逻辑转移到相关的VO中。企业Java实体应该愚蠢吗?

我刚刚和一位在Java EE方面比我有更多经验的同事讨论过,并且他证实了愚蠢的实体至少是过去推荐的方式。但是,他最近也读过了对这种立场的有效性提出质疑的意见。

据我所知,有至少限制了什么可以是实体类内部把问题:

  • 它不应该有直接依赖于数据层(例如查询代码而应进入单独的DAO)
  • 如果是直接暴露于较高的层或客户端(例如,经由SOAP),它的界面可能需要被限制

是否有任何更多的有效理由移动逻辑INT我的实体?还是考虑其他问题?

+1

实体是VO吗? – 2010-02-25 11:06:43

+0

@Pascal是的 - 请参阅我的更新。 – 2010-02-25 11:18:59

回答

17

DTOVO应该用于传输数据并且不嵌入逻辑。另一方面,业务对象应该嵌入一些逻辑。我说一些,因为总是有一个平衡点,可以找到你在服务中放置什么,这些服务协调涉及多个业务对象的逻辑和你在业务对象本身中放置的内容。业务对象中的典型逻辑可以是验证,字段计算或其他一次只影响一个业务对象的操作。

请注意,到目前为止,我还没有提到实体。持久实体通过ORM推广,我们现在试图同时使用持久实体作为DTO 业务对象。也就是说,实体本身在层和层之间流动,并包含一些逻辑。

是否有更多有效的理由不是 将逻辑移动到我的实体?还是要考虑其他问题?

正如您所指出的那样,这都是依赖关系和您所暴露的问题。只要实体是愚蠢的(靠近DTO),它们就可以很容易地被隔离在一个专用的罐子里,作为的API。你在实体中放置的逻辑越多,它就越难做到这一点。注意你所暴露的东西以及你依赖的东西(负载类,客户端也需要有依赖类)。这适用于例外情况,继承层次结构等。

仅举一个例子,我有一个项目,其中实体在业务层中使用方法toXml(...)。因此,实体的客户端依赖于XML。但如果你不太关心图层,并严格区分API和实现,我认为在实体中移动一些逻辑是很好的。

编辑

这个问题已经讨论了很多的时间和很可能会继续,因为没有明确的答案进行讨论。一些有趣的环节:

+0

我发现大多数有用的答案,很难挑选一个“最好的”。然而,我发现你是最平衡和最有洞察力的,所以你得到了复选标记:-)谢谢。 – 2010-03-01 21:37:08

+0

是的,经过一年多的重新阅读,我不得不同意ewernli肯定知道他在这个话题上的方法。 – 2011-08-09 21:18:06

8

我认为你的观点是有效的。
查看更多 - http://martinfowler.com/bliki/AnemicDomainModel.html
使用JPA,实体是轻量级对象。所以我不认为它有任何问题有逻辑。

在使用SOAP/Web服务的情况下,我会添加一个单独的Facade层。

+3

同意。使用只有getter和setter的对象完全不符合OO,更像是在C中使用结构。我喜欢C,但这是Java,我每次必须涉及一个类,其中不包含任何其他内容,而是表单中的样板代码getX()和setX() – 2010-02-25 11:43:13

+0

@Padmarag优秀的文章,谢谢:-) – 2010-02-25 12:28:21

+1

我希望它是这么简单:) – ewernli 2010-02-25 12:32:13

1

您所指的惯例是采用贫血域模型,而不是丰富的域模型,其中实体是简单的POJO注释为bean(@Entity),在获取者和设置者方面只有最小值。所以具有toXML()方法的类将被视为富域。我想这有助于保持清晰的视图,即使粒度不同,映射到关系数据库的内容也是如此。

其优点是逻辑和数据之间有明确的分离(这是OO哲学被破坏的地方)。方法是将这些分组到业务逻辑类或服务中。这是根据分层架构,其中各个层是域,服务,DAO和UI。

这就是理论。

编辑:只是为了澄清,当我说逻辑和数据之间存在明显的分离时,我的意思是一个对象是数据导向的,另一个是方法导向的方式,可以认为是程序的失效。

+2

@James P.如果你认为这是因为这个“破碎”,我恐怕你不太明白OO的观点:-( – 2010-02-25 14:06:54

+1

这是可能的,我总是开放的启蒙;)。纠正我,如果我错了,但听起来像封装原则是在这里辩论的中心。 – 2010-02-25 15:19:11

+0

P.我不知道我明白你的目标。无论如何,我不想就OO进行“辩论”,因为a)我宁愿将这个话题放在焦点上,b)关于OO的观点是(主观的)主观的和与上下文相关的,c)600个字符不足以满足这个要求无论如何。 – 2010-02-25 21:58:42

1

的主p为这些类添加逻辑的问题是,它们需要更多的属性来跟踪对象状态,而这些额外的属性通常不需要序列化。这意味着这些类的序列化机制需要额外的工作。

考虑到许多项目都混合了jr。程序员和sr。程序员和大部分工作是由不了解(或关心)最优序列化的jr执行的,将这些普通的旧java对象作为“值对象”很容易,它们几乎只是传递和接收数据,把逻辑放在其他地方。

如果您设法将逻辑放置在业务对象(即VO +逻辑)中,我认为这样做会更好。只要记住整个团队是在同一页面,他们不重复的代码和逻辑(这从来没有发生的权利?)

简短的回答:不,他们不应该总是愚蠢的,但是更容易有他们这样的方式。

+0

如果序列化存在问题,可以使其他属性为瞬态。没什么大不了的。 – 2010-02-25 17:32:09

+1

@Matthew当然,只要新手明白你在说什么。 – OscarRyz 2010-02-26 14:32:01

4

除了前面提到的Fowler文章之外,埃里克埃文斯的书域驱动设计(2004)有一篇丰富与贫血域模型的完整论文。

此外,检查出http://dddcommunity.org/

+1

你真的需要阅读这本书。这是工作既具有开创性又有用的罕见案例之一。 – HDave 2013-10-31 12:45:59

3

右键,下面是我从我的Java EE教练得到了反馈的摘要。

从实践的角度来看,只要这些方法与实体的属性一起工作,通过移动逻辑在贫血和富域之间达成妥协就不应成为问题。当涉及到一个丰富的领域时,这条线必须在某个地方绘制出来,显然福勒和国王都已经向这个方向发表了评论。

例如,考虑BankAccount中的一个calculateInterestRate()方法,该方法从其他域对象中提取信息,例如验证某人已经是客户端的时间。为了避免依赖,可以将对象分割为多个对象,但这种方式意味着代码可能会结束被多个类分散。在这一点上,人们不妨做一个InterestCalculator类。

要考虑的另一件事是线程安全。由Spring处理的Singleton DAO和服务应该是线程安全的,而域模型中的任何东西都会暴露给并发问题。

最后,还有维护问题。你确定你会在几年的时间内维护应用程序吗?您所做的选择似乎是合理的,但是您是否确信下一个开发人员将具备必要的专业知识,以便轻松理解您的代码?

0

我已经做了一些C编程,虽然OOP对于通过构建类层次结构来简化问题很好,但我仍然发现简单的C方法在很多情况下是最简单和最好的。在C中,我们只与公共成员结合,程序员将这些结构传递给函数(在Java中,这样的函数将是例如某些实用程序类中的静态函数),该函数操作成员。数据和算法是分开的,因为函数不是结构的成员。我一直有这样的感觉,即VO对象就像C中的结构体一样。当C语言不是最大的时候,有很多情况,例如,没有层次结构,没有多态性,体面的OOP程序员觉得有用。然而,一般来说,我喜欢这种简单的C方法,并且更喜欢使用它,除非我知道OOP技术会非常有益。例如。当我需要使用类层次结构来建模时,或者需要确保一个或多个类(层次结构中)的成员始终保持一致时,我不能使用C结构方法。但在这些情况下,我不仅会有制定者和获得者。

我还指这篇文章是关于C++,但我喜欢这家伙怎么解释这样的事情: http://www.gotw.ca/gotw/084.htm 这篇文章有什么时候做一个函数的类成员2个规则:

(从下面我留下了一些东西的报价,看过原著,如果你想看到所有)

  1. 务必使它的成员,如果它是一个:哪些操作必须是会员,因为C++语言只是这么说的(例如,c onstructors)还是由于功能原因(例如,它们必须是虚拟的)?如果他们必须是,那么哦,他们必须是;案件关闭。

  2. 如果需要访问内部部件,则需要使其成为会员:哪些操作需要访问我们必须通过友谊授予的内部数据?这些通常应该是成员。

在所有其他情况下,宁愿使它成为非成员非友人:哪些操作可以与非会员非友人一样工作?这些可以并因此通常应该是非成员。这应该是争取的默认情况。

我有这样的感觉,如果您不确定是否向这些类添加函数,那么您并不需要这么做。我在这里写的仅仅是为什么我不会为这些类添加方法的所有原因的一部分,但是也许这是我所有其他原因的父亲。但这是YMMV所有主观的。顺便提一句,静态效用函数的方法使单元测试在大多数情况下都很简单

0

实体频繁生成。在像Java这样的语言中,不存在部分类的事情,所以我们不能扩展一个实体(不考虑像Visitor这样的模式)。因此,当您需要重新生成实体时,必须再次添加业务逻辑,这实际上并不实际。

对于实体我宁愿去实体和逻辑之间的设计high cohesion

要考虑的其他问题是事实并非所有情况下实体的所有业务操作都是平等的。另外,允许实体中的业务逻辑倾向于使实体与XML等集成技术耦合,在我看来,这些技术应始终远离域逻辑。在洋葱体系结构中,XML将位于内部的外壳和域对象中,以说明如果要创建可重复使用的可插拔系统,它们实际上相互之间的距离有多远。