我最近无意中听到有人说数据传输对象(DTO) 是一种反模式。
为什么?有哪些替代方案?
有些项目有两次所有数据。一次作为域对象,一次作为数据传输对象。
这种重复需要付出巨大的代价,因此架构需要从这种分离中获得巨大的收益才值得。
DTO 不是反模式。当您通过网络发送一些数据(例如,发送到 Ajax 调用中的网页)时,您希望通过仅发送目的地将使用的数据来确保节省带宽。此外,表示层通常可以方便地使数据的格式与本机业务对象的格式略有不同。
我知道这是一个面向 Java 的问题,但在 .NET 语言中,匿名类型、序列化和 LINQ 允许动态构建 DTO,从而减少了使用它们的设置和开销。
EJB 3.0 之前的 EJB 规范中实体 Bean 的重量级特性导致了数据传输对象 (DTO) 等设计模式的使用。DTO 成为轻量级对象(本来应该是实体 bean 本身),用于跨层发送数据……现在 EJB 3.0 规范使实体 bean 模型与普通旧 Java 对象 (POJO) 相同。使用这个新的 POJO 模型,您将不再需要为每个实体或一组实体创建 DTO……如果您想跨层发送 EJB 3.0 实体,只需实现 java.io.Seraliazable
OO 纯粹主义者会说 DTO 是反模式,因为对象变成了数据表表示,而不是真正的域对象。
我不认为 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 是一种反模式,因为它们可能被滥用。它们经常在不应该/不需要的时候使用。
这篇文章模糊地描述了一些滥用行为。
如果你正在构建一个分布式系统,那么 DTO 肯定不是一种反模式。不是每个人都会在这个意义上进行开发,但如果你有一个(例如)开放社交应用程序,所有这些应用程序都运行 JavaScript。
它会将大量数据发布到您的 API。然后将其反序列化为某种形式的对象,通常是 DTO/Request 对象。然后可以对其进行验证,以确保输入的数据在转换为模型对象之前是正确的。
在我看来,它被视为一种反模式,因为它被误用了。如果您不构建分布式系统,那么您可能不需要它们。
问题不应该是“为什么”,而是“何时”。
当仅使用它的结果是更高的成本- 运行时或维护时,它绝对是反模式。我从事具有数百个与数据库实体类相同的 DTO 的项目。每次您想添加一个字段时,您都会添加四次 id - 到 DTO,到实体,从 DTO 到域类或实体的转换,反向转换,......您忘记了一些地方和数据不一致。
当您确实需要域类的不同表示形式时,这不是反模式——更扁平、更丰富、更窄......
就我个人而言,我从域类开始并传递它,并在正确的位置进行适当的检查。我可以注释和/或添加一些“帮助”类来制作映射、数据库、JSON 或 XML 等序列化格式……如果我觉得需要,我总是可以将一个类拆分为两个。
这是关于您的观点 - 我更喜欢将域对象视为扮演各种角色的单个对象,而不是从彼此创建的多个对象。如果一个对象的唯一角色是传输数据,那么它就是 DTO。
当您让所有域对象都急切地加载关联对象时,DTO 就成为必需品,而不是反模式。
如果您不制作 DTO,您将不必要地将对象从业务层传输到客户端/Web 层。
为了限制这种情况的开销,而是转移 DTO。
我认为人们的意思是,如果您将所有远程对象都实现为 DTO,这可能是一种反模式。DTO 只是一组属性,如果您有大对象,即使您不需要或不使用它们,您也总是会转移所有属性。在后一种情况下,更喜欢使用代理模式。