为什么数据传输对象(DTO)是反模式?

155
我最近听到人们说数据传输对象(DTO)是一种反模式。

为什么?有什么替代方案吗?


14
可能是因为业务对象本身能够传输它们自己的数据,非常感谢! - Zoidberg
18
"反模式"可能是我提名的"15分钟已经过去的短语"。现在,它的意思相当于"我不想费心证明我的想法",就像"众所周知..."一样。 - Craig Stuntz
7
送方法过程中传递对象给我们带来了CORBA、DCOM等经验,我尝试忘掉它们。但问题是,迟早会有人想要调用那些方法。 - Craig Stuntz
15
DTO代表DRY原则,不幸的是在J2EE中它意味着“做重复的事情”。 - joeforker
2
你可能想阅读这篇文章: 数据传输对象是耻辱 - yegor256
11个回答

173

一些项目将所有数据都复制了一遍。一份作为域对象,另一份作为数据传输对象。

这种重复造成了巨大的成本,因此架构需要从这种分离中获得巨大的好处才值得这样做。


7
请详细说明“巨大的成本”。此外,说明为什么无法通过使用代码生成技术来生成DTO类来消除成本。 - John Saunders
86
+1. 两次?只有你运气好的时候才会出现 :-) 将域实体复制为DTO的项目还往往会有几乎相同但微妙不同的UI Bean与之相配。这就是第三个。如果不幸存在某种远程传输(Web服务/XML-RPC/等等),你很容易就能达到四到五个。 - ChssPly76
20
我目前正在使用一种企业级14层千层面架构(不是开玩笑)。我可以保证,其中大约11层主要用于数据传输,并非免费获取。 - KarlP
13
此外,虽然我非常喜欢使用代码生成器来处理愚蠢的设计,但是它们有两个问题:a)它们肯定是从一开始就做错了。b)它们并不是免费的。 - KarlP
10
抱歉,但这个答案是错误的,而且错误的答案得到了高票和被接受。首先,您可以使用反射来动态生成DTO。其次,您可以使用“根定义”,例如在CASE系统或oAW中,并生成BO和DTO(s)。第三,您可以使用XSD和JAXB生成DTO,并使用DTO作为BO的基础,或者您可以从XSD生成两者...无论如何,如果有人敢将从数据库中新获取的EJB直接传输到客户端程序中,那么在我工作的环境中,他很快就会被解雇。 - Angel O'Sphere
显示剩余10条评论

148
DTOs不是一种反模式。当你通过网络发送数据(比如,在Ajax调用中发送到一个网页),你希望确保只发送目标会使用的数据,以节省带宽。此外,通常情况下,演示层希望以稍微不同的格式呈现数据,而不是原生的业务对象。
我知道这是一个面向Java的问题,但在.NET语言中,匿名类型、序列化和LINQ允许动态构建DTO,从而减少了设置和开销。

16
@John,这是不正确的。我经常这样做。序列化使用反射,在匿名类型上完全可以正常工作。只需将其作为对象传递给序列化器即可。一旦它被序列化(例如到xml或json),当然可以从方法中返回它。 - Gabe Moothart
9
约翰,那不是真的。你可以将匿名类型序列化为JSON。在MVC应用中尝试一下:return Json(new { Foo = "Hi there! } );我向你保证它完全可以正常工作。而且,也许比非匿名类型更好,因为匿名类型通常在对象图中没有循环引用,这会破坏JSON序列化器。 - Craig Stuntz
2
Json序列化在这个意义上不是DTO,因此不适用。 当然,我的上述论点也同样适用,但在某些时候,它们就不再值得了。 - Jim Barrows
2
Gabe是正确的,DTO本身并不是反模式(但如果使用不当可能会成为反模式!),当没有数据重复时。例如,BO可以将来自不同来源的数据聚合到DTO中。 - a.s.t.r.o
3
除了带宽节省之外,你还有许多其他原因需要使用DTO。你是否被公司政策或法律允许将每个字段都发送到网络上?此外,在我们公司中,我们的DAO非常复杂,因为它需要进行所有这些优化,作为服务层的工作人员,我很高兴他们使用DTO,让我们不必担心对象X与某个其他n对n表的关系。 - user64141
显示剩余2条评论

31

"EJB 3.0中的DTO是一种反模式"(原链接目前已下线)指出:

在EJB 3.0之前的EJB规范中,Entity Beans的重量级特性导致了使用数据传输对象(DTO)等设计模式。DTO成为轻量级对象(本应该是实体bean本身),用于在各层间发送数据……现在的EJB 3.0规范使实体bean模型与普通的Java对象(POJO)相同。有了这个新的POJO模型,您将不再需要为每个实体或一组实体创建DTO……如果您想要跨层发送EJB 3.0实体,请让它们实现java.io.Serializable接口


7
当您在同一JVM内存中的方法之间传输对象时,这是正确的。但当您实际上要通过网络进行序列化并希望控制序列化的深度和/或保留延迟加载时,则不正确。 - wrschneider
是的,即使在同一个JVM中,如果您使用JEE / Spring事务管理,也应该非常注意保持在同一个线程中。 - Marc

25

OO纯粹主义者会说DTO是反模式,因为对象变成了数据表格的表示,而不是真正的领域对象。


1
你还有什么其他方法可以将API视图模型暴露给前端? - Maulik Modi

23
我不认为DTO本身就是一种反模式,但与使用DTO相关的确实存在一些反模式。比尔·达德尼将DTO爆炸作为一个例子。

http://www.softwaresummit.com/2003/speakers/DudneyJ2EEAntiPatterns.pdf

这里还提到了一些 DTO 的滥用行为。

http://anirudhvyas.com/root/2008/04/19/abuses-of-dto-pattern-in-java-world/

它们的起源是由于三层系统(通常使用EJB作为技术)作为在层之间传递数据的手段。大多数基于现代框架(如Spring)的Java系统采用了一种替代简化的方式,使用POJO作为领域对象(通常使用JPA等注解),在单个层中进行操作... 在这里使用DTO是不必要的。

4
你说得完全正确,当数据传输对象(DTO)在适当的上下文中使用时,它们是一个好的模式而不是反模式。你提供的第二个链接似乎已经失效了,但我见过最常见的滥用情况是每个领域对象都有一个相应的“DTO”用于数据库交互,这并没有增加任何价值,也不是真正的DTO! - David
答案中的第二个链接已过时。 - Pang

10
问题不应该是“为什么”,而是“何时”。 绝对是当使用它的唯一结果是更高的成本 - 运行时或维护时,这是一个反模式。我曾经在项目中工作过,其中有数百个与数据库实体类相同的DTO。每当你想要添加一个字段时,你必须添加四次 - 到DTO,到实体,到从DTO到域类或实体的转换,反向转换...你忘记了其中一些地方,数据变得不一致。
当你真正需要不同表示方式的领域类时,这不是一个反模式 - 更扁平,更丰富等等。
就个人而言,我会从一个领域类开始并传递它,并在正确的位置进行适当的检查。我可以注释和/或添加一些“辅助”类来进行到数据库的映射,到JSON或XML等序列化格式的映射……如果需要的话,我总是可以将一个类拆分成两个。
这取决于你的观点 - 我更喜欢将领域对象看作是扮演各种角色的单个对象,而不是相互创建的多个对象。如果一个对象的唯一角色是传输数据,那么它就是DTO。

10

由于其可能被滥用,一些人认为DTO是反模式。它们经常在不必要的情况下使用。

这篇文章模糊地描述了一些滥用情况。


1
这篇文章更准确地将DTO描述为一种可能被滥用的模式。 - Craig Stuntz
1
这个回答只提供了链接,至少需要引用文章中的一句话。不知道为什么它没有被标记为“不是答案”。 - Nathan Hughes
1
链接失效,请尝试访问 https://web.archive.org/web/20110707143818/https://anirudhvyas.com/root/2008/04/19/abuses-of-dto-pattern-in-java-world/。 - Davi Lima
1
答案中的链接已过时。 - undefined
答案中的链接已过时。 - Pang

8
如果你正在构建分布式系统,那么DTO并不是反模式。并非每个人都会以这种方式开发,但如果你有一个(例如)基于JavaScript运行的Open Social应用程序,则会向API发布大量数据。然后将其反序列化为某种对象,通常是DTO /请求对象。然后可以验证该数据是否正确输入,然后将其转换为模型对象之前进行验证。在我看来,它被视为反模式,因为它被误用了。如果您没有构建分布式系统,那么很可能不需要使用它们。

6

当您的所有领域对象都急切地加载关联对象时,DTO变得必不可少,而不是反模式。

如果您不制作DTO,则会将不必要的传输对象从业务层传输到客户端/ Web层。

为了限制此情况下的开销,最好传输DTO。


1
或者,你知道的,可以在它们的类代码中定义一个视图来确定哪些对象被序列化或不被序列化。就像使用Jackson注释将对象转换为JSON一样。这样,您就不必重复执行所有操作或更多操作,并且不需要通过映射器(您还必须维护)来引入大量样板和垃圾。 - G_H

3

我认为人们的意思是,如果您将所有远程对象都实现为DTO,则可能会成为反模式。DTO仅仅是一组属性,如果您有大型对象,则即使您不需要或使用它们,也会传输所有属性。在后一种情况下,最好使用代理模式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接