【问题标题】:What is a Data Transfer Object (DTO)?什么是数据传输对象 (DTO)?
【发布时间】:2010-11-06 06:37:16
【问题描述】:

什么是数据传输对象?

在 MVC 中是模型类 DTO,如果不是,有什么区别?我们需要两者吗?

【问题讨论】:

    标签: model-view-controller architecture dto data-transfer data-transfer-objects


    【解决方案1】:

    使用 MVC 数据传输对象通常用于将域模型映射到更简单的对象,这些对象最终将由视图显示。

    来自Wikipedia

    数据传输对象 (DTO),以前称为值对象或 VO,是 一种用于在软件应用程序之间传输数据的设计模式 子系统。 DTO 通常与数据访问结合使用 对象从数据库中检索数据。

    【讨论】:

    • 值对象不是DTO
    【解决方案2】:

    DTO 的定义可以在Martin Fowler's site 上找到。 DTO 用于将参数传递给方法并作为返回类型。很多人在 UI 中使用它们,但其他人从它们中扩充域对象。

    【讨论】:

      【解决方案3】:

      DTO 是一个愚蠢的对象 - 它只包含属性并具有 getter 和 setter,但没有其他任何重要的逻辑(可能是 compare()equals() 实现)。

      MVC 中的典型模型类(假设此处为 .net MVC)是 DTO,或 DTO 的集合/聚合

      【讨论】:

      • 你描述的是一个LocalDTO:martinfowler.com/bliki/LocalDTO.html
      • 使用 DTO 之类的东西很有用的一种情况是,当您的表示层中的模型与底层域模型之间存在严重不匹配时。在这种情况下,使表示特定的外观/网关从域模型映射并呈现便于表示的界面是有意义的。
      【解决方案4】:

      数据传输对象是用于封装数据并将其从应用程序的一个子系统发送到另一个子系统的对象。

      N 层应用程序中的服务层最常使用 DTO 在其自身和 UI 层之间传输数据。这里的主要好处是它减少了分布式应用程序中需要通过网络发送的数据量。他们还在 MVC 模式中制作了出色的模型。

      DTO 的另一个用途是封装方法调用的参数。如果一个方法需要四个或五个以上的参数,这会很有用。

      当使用 DTO 模式时,您还可以使用 DTO 汇编器。汇编器用于从域对象创建 DTO,反之亦然。

      从域对象到 DTO 再转换回来可能是一个代价高昂的过程。如果您不创建分布式应用程序,您可能不会从该模式中看到任何巨大的好处,例如 Martin Fowler explains here

      【讨论】:

      • “DTO 在 MVC 模式中制作出色的模型”——但模型不应该包含对象的所有数据,并且 DTO 不应该使用部分数据进行优化吗?如果我有模型 A 并且我需要将它传递给两个子系统,是否会有 A_DTO_1 和 A_DTO_2 以及每个子系统的相关字段? “DTO 可以是为方法调用封装参数” --> 那么每个封装参数的类都是 DTO,即使这不是分布式系统? MVC 中的模型不是领域对象吗?
      • 在回答您的第一个问题时,我认为所谈论的不是同一件事。 MVC 中的模型不一定需要是您的领域模型中的一个类。话虽如此,它很可能是。使用 DTO 去除了所有不必要的东西。仅取决于您要使用的架构。我不确定如何回答你的第二个问题。无论是否通过网络,它仍然是一个对象,它封装了要在(子)系统之间传输的一堆数据,所以我认为它是一个 DTO。
      • “DTO 的另一个用途是封装方法调用的参数。如果方法采用超过 4 或 5 个参数,这将很有用。”这实际上是一种称为 Poltergeist 或 Gypsy Wagon 类的反模式。如果你的方法需要 4 个参数,那就给它 4 个参数,不要仅仅为了将对象移动到方法或类中而创建一个类。
      • @Wix,好点。但是,我认为如果它在语义上是正确的(例如,如果您传递带有属性而不是属性本身作为值的设置类),这是可以的。你不应该做的是为了传递单个对象而抛出所有参数,因为它们很可能是不相关的,并导致以后的噩梦解开。
      • DTO 不应该用于封装方法调用的参数(这会使它们成为 LocalDTO),它们是在远程接口的上下文中引入的:martinfowler.com/bliki/LocalDTO.html
      【解决方案5】:

      一般值对象应该是不可变的。像 Java 中的 IntegerString 对象。我们可以使用它们在软件层之间传输数据。如果软件层或服务在不同的远程节点中运行,例如在微服务环境或旧版 Java 企业应用程序中。我们必须几乎完全复制两个类。这就是我们遇到 DTO 的地方。

      |-----------|                                                   |--------------|
      | SERVICE 1 |--> Credentials DTO >--------> Credentials DTO >-- | AUTH SERVICE |
      |-----------|                                                   |--------------|
      

      在旧版 Java 企业系统中,DTO 可以包含各种 EJB 内容。

      我不知道这是否是最佳实践,但我个人在我的 Spring MVC/Boot 项目中使用 Value Objects,如下所示:

              |------------|         |------------------|                             |------------|
      -> Form |            | -> Form |                  | -> Entity                   |            |
              | Controller |         | Service / Facade |                             | Repository |
      <- View |            | <- View |                  | <- Entity / Projection View |            |
              |------------|         |------------------|                             |------------|
      

      Controller 层不知道实体是什么。它与 FormView Value Objects 通信。表单对象具有 JSR 303 验证注释(例如 @NotNull),View Value Objects 具有用于自定义序列化的 Jackson 注释。 (例如@JsonIgnore)

      服务层通过使用实体对象与存储库层进行通信。实体对象上有 JPA/Hibernate/Spring Data 注释。每层仅与较低层通信。由于循环/循环依赖,层间通信被禁止。

      User Service ----> XX CANNOT CALL XX ----> Order Service
      

      一些 ORM 框架具有通过使用额外的接口或类进行投影的能力。因此存储库可以直接返回 View 对象。在那里你不需要额外的转换。

      例如这是我们的用户实体:

      @Entity
      public final class User {
          private String id;
          private String firstname;
          private String lastname;
          private String phone;
          private String fax;
          private String address;
          // Accessors ...
      }
      

      但您应该返回一个仅包含 id、firstname、lastname 的用户分页列表。然后你可以为 ORM 投影创建一个 View Value Object。

      public final class UserListItemView {
          private String id;
          private String firstname;
          private String lastname;
          // Accessors ...
      }
      

      您可以轻松地从存储库层获取分页结果。感谢 spring,您还可以只使用接口进行投影。

      List<UserListItemView> find(Pageable pageable);
      

      不用担心其他转换操作BeanUtils.copy 方法工作正常。

      【讨论】:

        【解决方案6】:
        1. 对我而言,什么是 DTO 问题的最佳答案是,DTO 是简单的对象,不应包含任何业务逻辑或方法实现需要测试
        2. 通常您的模型(使用 MVC 模式)是智能模型,它们可以包含很多/一些方法,专门针对该模型执行一些不同的操作(不是业务逻辑,这应该在控制器中)。但是,当您传输数据时(例如,从某处调用 REST (GET/POST/whatever) 端点,或使用 SOA 使用 Web 服务等...)您不想使用端点不需要的代码会消耗数据并减慢传输速度。

        【讨论】:

        • 为什么业务逻辑应该在控制器中?
        • @Thiago Burgos 您的意思是“在服务中”吗?
        【解决方案7】:

        数据传输对象(DTO)描述了“一个携带数据的对象 进程之间”(维基百科)或“用于封装数据的对象, 并将其从应用程序的一个子系统发送到另一个”(堆栈溢出 回答)。

        【讨论】:

          【解决方案8】:

          DefN

          DTO 是一个硬编码数据模型。它只解决了对由硬编码生产过程处理的数据记录进行建模的问题,其中所有字段在编译时都是已知的,因此可以通过强类型属性访问。

          相比之下,动态模型或“属性包”解决了在运行时创建生产过程时对数据记录进行建模的问题。

          Cvar

          可以使用字段或属性对 DTO 进行建模,但有人发明了一种非常有用的数据容器,称为 Cvar。它是对值的引用。当 DTO 使用我称之为 reference properties 的东西建模时,可以将模块配置为共享堆内存,从而协作处理它。这完全消除了代码中的参数传递和 O2O 通信。换句话说,具有引用属性的 DTO 允许代码实现零耦合

              class Cvar { ... }
          
              class Cvar<T> : Cvar
              {
                  public T Value { get; set; }
              }
          
              class MyDTO
              {
                  public Cvar<int> X { get; set; }
                  public Cvar<int> Y { get; set; }
                  public Cvar<string> mutableString { get; set; } // >;)
              }
          

          来源:http://www.powersemantics.com/

          动态 DTO 是动态软件的必要组件。为了实例化一个动态过程,一个编译器步骤是将脚本中的每台机器绑定到脚本定义的引用属性。通过将 Cvar 添加到集合中来构建动态 DTO。

              // a dynamic DTO
              class CvarRegistry : Dictionary<string, Cvar> { }
          

          争用

          注意:因为 Wix 将使用 DTO 组织参数标记为“反模式”,所以我将给出权威意见。

              return View(model);  // MVC disagrees
          

          我的协作架构取代了设计模式。请参阅我的网络文章。

          参数提供对堆栈帧机器的直接控制。如果您使用连续控制,因此不需要立即控制,则您的模块不需要参数。我的架构没有。当参数是值类型时,机器(方法)的进程内配置增加了复杂性,但也增加了价值(性能)。但是,引用类型参数会使消费者导致缓存未命中以从堆中获取值——因此,只需使用引用属性配置消费者。来自机械工程的事实:依赖参数是一种预优化,因为加工(制造部件)本身就是浪费。有关详细信息,请参阅我的 W 文章。 http://www.powersemantics.com/w.html.

          如果 Fowler 和他的公司了解任何其他架构,他们可能会意识到 DTO 在分布式架构之外的好处。程序员只知道分布式系统。集成协作系统(又名生产又名制造)是我不得不声称自己的架构,因为我是第一个以这种方式编写代码的人。

          有些人认为 DTO 是一个贫乏的领域模型,这意味着它缺乏功能,但这假设一个对象必须拥有它与之交互的数据。然后,这个概念模型迫使您在对象之间传递数据,这是分布式处理的模型。然而,在生产线上,每个步骤都可以访问最终产品并对其进行更改,而无需拥有或控制它。这就是分布式处理和集成处理之间的区别。制造将产品与运营和物流分开。

          将处理建模为一群无用的办公室工作人员,他们在不保留电子邮件跟踪的情况下相互发送电子邮件工作,这并没有本质上的错误,除了在处理物流和退货问题时产生的所有额外工作和头痛。一个正确建模的分布式流程将一个文档(主动路由)附加到产品上,描述它来自和将去往的操作。活动路由是流程源路由的副本,它是在流程开始之前编写的。如果发生缺陷或其他紧急更改,则修改活动路由以包括将发送到的操作步骤。这就是投入生产的所有劳动力。

          【讨论】:

            【解决方案9】:

            数据传输对象背后的原理是创建仅包含特定数据事务所需的必要属性的新数据对象。

            好处包括:

            让数据传输更安全 如果删除所有不必要的数据,请减小传输大小。

            阅读更多:https://www.codenerd.co.za/what-is-data-transfer-objects

            【讨论】:

              【解决方案10】:

              我会向我的孩子解释 DTO

              我儿子数据传输对象(又名 DTO)用于封装我们从一个应用程序(api 等)发送到另一个应用程序的数据
              使用 DTO 为您的系统定义输入和输出接口

              这里所说的系统是指您有多个端点(webapps、api 等),它们共同构成一个系统

              【讨论】:

                【解决方案11】:

                所有学分都归Rick-Andreson

                生产应用程序通常会限制使用模型子集输入和返回的数据。这背后有多种原因,安全性是一个主要原因。模型的子集通常称为数据传输对象 (DTO)、输入模型或视图模型。

                DTO 可用于:

                • 防止过度发布。
                • 隐藏客户不应查看的属性。
                • 省略某些属性以减小负载大小。
                • 扁平化包含嵌套对象的对象图。
                • 扁平化的对象图对客户来说更方便。

                DTO 方法的实际实现,Rick-AndresonMicrosoft Web APIs best tutorials and practices 上使用 C# 和 ASP .Net Core 5:

                【讨论】:

                  【解决方案12】:

                  一些程序员使用 DTO 来区分将通过 API 传递的最终对象数据。因此,它基本上是端点的有效负载对象。例如,您可以将传递给服务器的联系表单值对象命名为contactFormDto or contactFromPayload,然后您或任何其他程序员都知道您在该对象中拥有的是数据的最终形状,它将通过网络传播。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2010-11-28
                    • 2011-01-04
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多