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


132

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

为什么?有哪些选择?


11
也许因为业务对象本身能够传输自己的数据,所以非常感谢!
Zoidberg

13
我的“反模式”很可能是“很久以前就有15分钟发言的短语”的提名人。到目前为止,它是“我不在乎为我的思想辩护”的同义词,例如“众所周知……”
Craig Stuntz 2009年

6
Zoidberg通过有线方式发送对象给了我们CORBA,DCOM和其他我尝试抹去记忆的经验。问题是,人们迟早要调用这些方法。
Craig Stuntz 2009年

10
DTO的体现DRY原则,不幸的是在J2EE代表重复自己。
joeforker

您可能需要阅读以下内容:数据传输对象是一种耻辱
yegor256

Answers:


139

有些项目的所有数据都有两次。一次作为域对象,一次作为数据传输对象。

这种重复需要付出巨大的代价,因此该架构需要从这种分离中获得巨大的收益,才能使其值得。


5
请详细说明“巨额费用”。另外,说一下为什么不能通过使用代码生成技术来生成DTO类来消除成本。
约翰·桑德斯

70
+1。两次?仅当您很幸运时:-)将域实体复制为DTO的项目也倾向于使用几乎相同但又如此微妙的UI Bean来对其进行补充。那是3。而且,上帝禁止,如果正在执行某种远程处理(Web服务/ xml-rpc /任何东西),则可以轻松达到4或
5。– ChssPly76

17
我目前正在使用一种企业级14层烤宽面条体系结构(不搭便车)。我可以向您保证,主要用于数据传输器的11层左右不是免费提供的。
2009年

10
另外,尽管我绝对喜欢在使用愚蠢的设计时使用代码生成器,但是它们是a)确定您一开始做错了。b)他们不是免费来的。
KarlP

7
抱歉,但这只是不正确,错误答案被高票通过并被接受。首先,您可以使用反射来动态生成DTO。其次,您可以在CASE系统或oAW中使用“根定义”,并生成BO和DTO。第三,您可以使用XSD和JAXB生成DTO并将DTO用作BO的基础,或者您也可以从XSD生成两者...如果有人敢于通过DB刚从数据库中获取新的EJB,连线到客户端程序...在我工作的环境中,他的头很快就会落在银板上...
Angel O'Sphere 2011年

128

DTO不是反模式。通过网络发送某些数据(例如,通过Ajax调用发送到网页)时,您要确保仅发送目标将使用的数据来节省带宽。同样,对于表示层来说,以与本机业务对象略有不同的格式存储数据通常很方便。

我知道这是一个面向Java的问题,但是在.NET语言中,匿名类型,序列化和LINQ允许即时构建DTO,从而减少了设置和使用它们的开销。


15
@John,这是不正确的。我一直都这样做。序列化使用反射,它在匿名类型上工作得很好。只需将其作为对象传递给序列化器即可。并且一旦将其序列化(例如,转换为xml或json),您就可以从方法中将其返回。
加布·穆阿特2009年

8
恩,约翰,那不是真的。您可以将匿名类型序列化为JSON就可以了。在MVC应用程序中尝试:return Json(new {Foo =“ Hi there!});我向您保证它可以正常工作。也许比非匿名类型更好,因为匿名类型在对象图中通常没有循环。 ,这打破了JSON序列化。
克雷格斯顿茨

1
从这个意义上讲,Json序列化不是DTO,因此不适用。然后,当然,我上面的论点同样适用,在某些时候,它们只是不值得。
Jim Barrows

1
Gabe是正确的,在没有数据重复的情况下,DTO本身并不是反模式(但如果使用不正确,则可以使用反模式!)。例如,BO可以将来自不同来源的数据聚合到DTO中。
astro

3
+1。除了带宽节省之外,还有其他许多原因需要DTO。您甚至允许(根据公司政策或法律)通过网络发送每个字段吗?另外,在我们公司中,DAO非常复杂,因为它需要进行所有这些优化-在服务层上工作,我很高兴他们使用DTO,并且不必担心我们与Object X的关系到其他一些N对N表。
user64141 2014年

25

DTO EJB 3.0中的AntiPattern表示:

在EJB 3.0之前的EJB规范中,实体Bean具有很重的性质,导致使用了诸如数据传输对象(DTO)之类的设计模式。DTO成为了轻量级对象(首先应该是实体bean本身),用于跨层发送数据...现在EJB 3.0规范使Entity bean模型与普通旧Java对象(PO​​JO)相同。使用这个新的POJO模型,您将不再需要为每个实体或一组实体创建DTO ...如果要跨层发送EJB 3.0实体,则使它们只需实现java.io.Serialiazable


6
当您在内存中的同一JVM中的方法之间传输对象时为true。当您实际上在通过有线进行序列化并且想要控制序列化的深度和/或保留延迟加载时,情况并非如此。
wrschneider 2012年

是的,即使在同一个JVM中,如果您使用JEE / Spring事务管理,您也应该真正留在同一个主题中。
2014年

20

我不认为DTO本身就是反模式,但是存在与DTO的使用相关的反模式。Bill Dudney以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。


3
完全正确的是,在正确的上下文中使用DTO时,它是一种很好的模式,而不是一种反模式。您的第二个链接现在似乎已失效,但是我看到的最大滥用是每个域对象都有一个对应的“ DTO”用于数据库交互,这没有任何价值,而且根本不是DTO!
大卫


9

有些人认为DTO可能会滥用,因此是一种反模式。通常在不需要/不需要时使用它们。

本文模糊地描述了一些滥用行为。


1
文章更准确地将DTO描述为一种可以滥用的模式。
Craig Stuntz 2009年

这相当于一个仅链接的答案,实际上至少需要引用该文章中的一个引号。不知道如何设法避免将其标记为“不是答案”。
内森·休斯


7

如果要构建分布式系统,则DTO肯定不是反模式。并非每个人都会在这种意义上发展,但是,如果您拥有(例如)全部运行JavaScript的Open Social应用程序。

它将大量数据发布到您的API。然后将其反序列化为某种形式的对象,通常是DTO / Request对象。然后可以对此进行验证,以确保输入的数据正确之后再转换为模型对象。

在我看来,由于使用不当,它被视为反模式。如果您不构建分布式系统,则可能不需要它们。


4

一个的意图数据传输对象是存储来自不同来源的数据,然后将其转移到一个数据库(或远程门面在一次)。

但是,DTO模式违反了“ 单一职责原则”,因为DTO不仅存储数据,而且还将数据从数据库/门面传输或传输到数据库/门面。

将数据对象与业务对象分离的需求不是反模式,因为无论如何可能都需要分离数据库层

代替DTO,您应该使用“聚合”和“存储库模式”,这将对象的集合(Aggregate)和数据传输(Repository)分开了。

要传输一组对象,您可以使用工作单元模式,该模式包含一组存储库和一个事务上下文。为了在交易中分别转移集合中的每个对象。


4

当您让所有域对象更快速地加载关联的对象时,DTO成为必需品,而不是一个ANTI-PATTERN。

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

为了限制这种情况的开销,请转移DTO。


1

问题不应该是“为什么”,而应该是“ 何时 ”。

最终,当仅使用它的结果是较高的成本(运行时或维护)时,它就是反模式。我从事的项目具有与数据库实体类相同的数百个DTO。每次您想要添加一个字段时,您的广告都会向IDTO添加四倍的ID-到DTO,到实体,从DTO到领域类或实体的转换,逆向转换... ...您忘记了获得的一些位置和数据不一致的。

当您确实需要域类的不同表示形式时,不是反模式 -更平坦,更丰富,更狭窄,...

我个人是从域类开始的,并在正确的地方进行了适当的检查,并通过了它。我可以注释和/或添加一些“帮助程序”类,以进行映射,到数据库以及诸如JSON或XML的序列化格式……如果需要,我总是可以将一个类拆分为两个。

这是关于您的观点的-我更喜欢将域对象看作是扮演各种角色的单个对象,而不是彼此创建的多个对象。如果对象唯一的角色是传输数据,那么它就是DTO。


1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.