【问题标题】:Why are data transfer objects (DTOs) an anti-pattern?为什么数据传输对象 (DTO) 是反模式?
【发布时间】:2009-09-17 19:46:34
【问题描述】:

我最近无意中听到有人说data transfer objects (DTO) 是一种反模式

为什么?有哪些替代方案?

【问题讨论】:

  • 也许是因为业务对象本身能够传输自己的数据,非常感谢!
  • “反模式”很可能是我提名的“15 分钟很久以前的短语”。它现在是“我不在乎为自己的想法辩护”的同义词,比如“众所周知......”
  • Zoidberg,通过网络发送带有方法的对象给了我们 CORBA、DCOM 和其他我尝试抹去记忆的经验。问题是,人们迟早要调用这些方法。
  • DTO 体现了 DRY 原则,不幸的是,它在 J2EE 中代表 do repeat yourself。
  • 您可能想阅读以下内容:Data Transfer Object Is a Shame

标签: java ejb dto anti-patterns data-transfer-objects


【解决方案1】:

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

这种重复会带来巨大的成本,因此架构需要从这种分离中获得巨大的收益才值得。

【讨论】:

  • 请详细说明“巨额费用”。另外,说明为什么不能通过使用代码生成技术来生成 DTO 类来消除成本。
  • +1。两次?仅当您很幸运 :-) 将域实体复制为 DTO 的项目也往往具有几乎相同但非常微妙不同的 UI bean 来补充它们。那是 3。如果,上帝保佑,有某种远程处理(Web 服务 / xml-rpc / 其他)正在进行,你可以轻松达到 4 或 5。
  • 我目前正在使用一个企业级的 14 层千层面架构(不是开玩笑)。我可以向你保证,主要用于数据传输的 11 层左右并不是免费提供的。
  • 另外,虽然我绝对喜欢在处理愚蠢的设计时使用代码生成器,但它们是 a) 肯定你一开始就做错了。 b) 他们不是免费来的。
  • 抱歉,这是不正确的,错误的答案被高票接受。首先,您可以使用反射动态生成 DTO。其次,您可以使用“根定义”,例如在 CASE 系统或 oAW 中生成 BO 和 DTO。第三,您可以使用 XSD 和 JAXB 生成 DTO 并将 DTO 用作 BO 的基础,或者您可以从 XSD 生成两者……无论如何,如果有人敢将从数据库中新获取的 EJB 传输过来连接到客户端程序......在我工作的环境中,他的头很快就会在银盘上......
【解决方案2】:

DTO 不是反模式。当您通过网络发送一些数据时(例如,发送到 Ajax 调用中的网页),您希望通过仅发送目的地将使用的数据来确保节省带宽。此外,表示层的数据格式与本机业务对象的格式略有不同,这通常很方便。

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

【讨论】:

  • @John,这是不正确的。我一直都这样做。序列化使用反射,它在匿名类型上工作得很好。只需将其作为对象传递给序列化程序即可。一旦它被序列化(例如,xml 或 json),你当然可以从方法中返回它。
  • 嗯,约翰,这不是真的。您可以将匿名类型序列化为 JSON 就好了。在 MVC 应用程序中尝试: return Json(new { Foo = "Hi there! } ); 我向你保证它工作得很好。也许比非匿名类型更好,因为匿名类型通常在其对象图中没有循环,这会破坏 JSON 序列化程序。
  • Json 序列化在这个意义上不是 DTO,因此不适用。当然,我上面的论点也适用,在某些时候,它们不再值得了。
  • Gabe 是对的,在没有数据重复的情况下,DTO 本身并不是一种反模式(但如果使用不当,可能会成为反模式!)。例如,BO 可以将来自不同来源的数据聚合到 DTO 中。
  • +1。除了节省带宽之外,还有很多其他原因需要 DTO。您甚至允许(根据公司政策或法律)通过网络发送每个字段吗?另外,在我们公司,我们的 DAO 非常复杂,因为它需要进行所有这些优化——在服务层工作,我真的很高兴他们使用 DTO 并且不用担心对象 X 的关系到其他 n 对 n 表。
【解决方案3】:

“DTO an AntiPattern in EJB 3.0”(原始链接目前离线)说:

Entity 的重量级性质 之前 EJB 规范中的 Bean EJB 3.0,导致使用 数据传输等设计模式 对象 (DTO)。 DTO 成为 轻量级对象(应该有 实体豆本身在 第一个地方),用于发送 跨层的数据...现在是 EJB 3.0 规范使实体 bean 模型相同 作为普通的旧 Java 对象 (POJO)。和 这种新的 POJO 模型,你不会 不再需要为每个 实体或一组实体...如果 您想发送 EJB 3.0 实体 跨层让他们只是 实现 java.io.Seraliazable

【讨论】:

  • 当您在内存中的同一 JVM 中的方法之间传输对象时为真。当您实际通过网络进行序列化并希望控制序列化深度和/或保留延迟加载时,情况并非如此。
  • 是的,即使在同一个 JVM 中,如果您使用 JEE/Spring 事务管理,您也应该真正注意保持在同一个线程中。
【解决方案4】:

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

【讨论】:

  • 您还会如何向前端公开 API 视图模型?
【解决方案5】:

我不认为 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。

【讨论】:

  • 当在适当的上下文中使用 DTO 时,您完全正确的是 a good pattern,而不是反模式。您的第二个链接现在似乎死了,但我见过的最大的滥用是每个域对象都有一个相应的“DTO”用于数据库交互,它没有增加任何价值,根本不是 DTO!
【解决方案6】:

有些人认为 DTO 是一种反模式,因为它们可能被滥用。它们经常在不应该/不需要的时候使用。

This article 模糊地描述了一些滥用行为。

【讨论】:

【解决方案7】:

如果您正在构建分布式系统,那么 DTO 肯定不是一种反模式。不是每个人都会在这个意义上进行开发,但是如果你有一个(例如)开放社交应用程序,所有这些应用程序都运行 JavaScript。

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

在我看来,它被视为一种反模式,因为它被误用了。如果您不构建分布式系统,那么您可能不需要它们。

【讨论】:

    【解决方案8】:

    当您让所有域对象都急切地加载关联对象时,DTO 成为必需品,而不是 ANTI-PATTERN。

    如果您不制作 DTO,您将不必要地将对象从业务层转移到客户端/Web 层。

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

    【讨论】:

    • 或者,你知道,以某种方式为那些可以在它们的类代码中完成的对象定义一个视图,以确定什么被序列化。就像将对象转换为 JSON 的 Jackson 注释一样。这样你就不必重复或更多地做所有事情,并以映射器的形式引入更多的样板和杂物(然后你还必须维护)。
    【解决方案9】:

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

    绝对是反模式,当仅使用它的结果是更高的成本时 - 运行时或维护。我从事具有数百个与数据库实体类相同的 DTO 的项目。每次您想添加一个字段时,您要添加四次 id - 到 DTO,到实体,从 DTO 到域类或实体的转换,反向转换,...​​...您忘记了一些地方和数据不一致。

    当您确实需要不同的领域类表示时,它不是反模式 - 更扁平、更丰富、更窄......

    我个人从域类开始并传递它,并在正确的位置进行适当的检查。我可以注释和/或添加一些“帮助”类来进行映射、数据库、JSON 或 XML 等序列化格式……如果我觉得需要,我总是可以将一个类拆分为两个。

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

    【讨论】:

      【解决方案10】:

      Data Transfer Object 的目的是存储来自不同来源的数据,然后立即将其传输到数据库(或Remote Facade)中。

      但是,DTO 模式违反了Single Responsibility Principle,因为 DTO 不仅存储数据,还从数据库/外观传输数据或将数据传输到数据库/外观。

      需要将数据对象与业务对象分开并不是一种反模式,因为无论如何separate the database layer 可能都需要它。

      您应该使用聚合和存储库模式而不是 DTO,它将对象集合 (Aggregate) 和数据传输 (Repository) 分开。

      要传输一组对象,您可以使用Unit Of Work 模式,它包含一组存储库和一个事务上下文;为了在事务中单独传输聚合中的每个对象。

      【讨论】:

      • " DTO 不仅存储数据,而且还从或向数据库/外观传输数据。" 根据 SRP,您如何建议在不存储任何表示的情况下传输数据?我想你会告诉我一个有两个字段的类违反了 SRP,因为它存储了两个东西,而不是一个东西?
      【解决方案11】:

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

      【讨论】:

        猜你喜欢
        • 2010-11-06
        • 2012-09-15
        • 1970-01-01
        • 2010-11-28
        • 2011-01-04
        • 2014-01-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多