上下文

您正在设计一个分布式应用程序,为了满足单个客户端请求,您发现自己对一个远程接口发出了多个调用,而这些调用所增加的响应时间超出了可接受的程度。

问题

如何保留过程调用接口的简单语义,而不受远程通信固有的滞后时间问题的影响?

影响因素

在与远程对象通信时,请考虑下列需要权衡的因素:

"

远程调用(那些必须跨越网络的调用)速度缓慢。虽然许多远程调用框架可以隐藏进行远程调用的复杂性,但是它们不能消除发生通信所需的步骤。例如,必须先找到远程对象位置,而且建立与远程计算机的连接,然后才能将数据串行化为字节流,然后可能进行加密,最后才能将其传输到远程计算机。

"

在考虑网络性能时,必须同时考虑滞后时间和吞吐量。简单地说,"滞后时间"描述了数据的首字节到达目的地之前所经过的时间。"吞吐量"描述了在某个时间段(例如 1 秒)内通过网络发送的数据字节数。在基于 IP 路由的现代网络(例如 Internet)中,滞后时间可以是比吞吐量更大的因素。这意味着,传输 10 字节数据所用的时间可能几乎等于传输 1,000 字节数据所用的时间。在使用无连接协议(如 HTTP)时,此效果尤其明显。通常,网络速度越快可以使吞吐量得以增加,但是,要减少滞后时间则会更加困难。

"

在设计对象接口时,好的做法是将大量信息隐藏在对象内,并提供一组细粒度方法来访问和操作该信息。"细粒度"意味着每个方法都应该负责单个的、相当小的和基本的功能单位。此方法简化了编程,并提供了对对象内部的更佳抽象,从而增加了重用的可能性。必须根据以下事实对此进行平衡取舍:使用较细粒度的方法意味着需要调用更多的方法才能执行高级别的任务。通常,在同一进程内调用方法时,这些额外函数调用的开销是可接受的;但是,在跨进程和网络边界调用这些方法时,开销可能变得难以接受。

"

避免远程调用中固有的滞后时间问题的最佳方法是进行更少的调用,并让每个调用传递更多的数据。做到这一点的一种方法是,使用长参数列表来声明远程方法。这样,客户端就可以在单个调用中将更多的信息传递给远程组件。但是,这样做会使针对此接口的编程容易出错,因为程序很可能仅按调用语句中的位置来调用外部方法的参数。例如,如果远程方法接受 10 个字符串参数,则开发人员很容易按错误顺序传递参数。编译器将无法检测到这样的错误。

"

长参数列表无助于从远程调用向客户端返回更多的信息,因为大多数的编程语言将方法调用的返回类型限制为单个参数。而巧合的是,在传输大多数数据时通常需要返回较多信息。例如,许多用户接口传输少量的信息,却希望返回大量结果数据。

解决方案

创建一个数据传输对象 (DTO),用该对象包含远程调用所需要的所有数据。修改远程方法签名,以便将 DTO 作为单个参数接受,并将单个 DTO 参数返回给客户端。在调用方应用程序收到 DTO 并将其作为本地对象存储之后,应用程序可以分别对 DTO 发出一系列单独的过程调用,而不会引发远程调用开销。Martin Fowler 在Patterns of Enterprise Application Architecture[Fowler03] 中对此模式进行了说明。

下图显示客户端应用程序如何进行一系列远程调用以检索客户名称的各个元素。

图 1:没有DTO的远程调用

DTO 允许远程对象在单个远程调用中将整个客户名称返回给客户端。在此示例中,这样做将使调用次数从 4 次减为 1 次。客户端进行单个调用,然后在本地与 DTO 交互,而不用进行多次远程调用(见图 2)。

图 2:通过使用DTO减少调用次数

DTO 是一组需要跨进程或网络边界传输的聚合数据的简单容器。它不应该包含业务逻辑,并将其行为限制为诸如内部一致性检查和基本验证之类的活动。注意,不要因实现这些方法而导致 DTO 依赖于任何新类。

在设计数据传输对象时,您有两种主要选择:使用一般集合;或使用显式的 getter 和 setter 方法创建自定义对象。

一般集合的优点是,只需要一个类,就可以在整个应用程序中满足任何数据传输目的。此外,集合类(例如,简单数组或散列图)内置于几乎所有语言库中,因此您根本不必编写新类的代码。对 DTO 使用集合对象的主要缺点是,客户端必须按位置序号(在简单数组的情况下)或元素名称(在键控集合的情况下)访问集合内的字段。此外,集合存储的是同一类型(通常是最一般的Object类型)的项目,这可以导致在编译时无法检测到的微妙但致命的编码错误。

如果为每个 DTO 创建自定义类,则可以提供与任何其他对象完全一样的、客户端应用程序可访问的强类型对象,这样的对象可以提供编译时检查,并支持代码编辑器功能(如 Microsoft® IntelliSense® 技术)。主要缺点是,如果应用程序发出许多远程调用,则您最终可能必须编写大量类的代码。

许多方法试图将这两种方法的优点结合在一起。第一种方法是代码生成技术,该技术可以生成脱离现有元数据(如可扩展标记语言 (XML) 架构)的自定义 DTO 类的源代码。第二种方法是提供更强大的集合,尽管它是一般的集合,但它将关系和数据类型信息与原始数据存储在一起。Microsoft ADO.NETDataSet 支持这两种方法(请参阅在 .NET 中使用 DataSet 实现 Data Transfer Object)。

有了 DTO 类以后,需要用数据填充它。大多数情况下,DTO 内的数据来自多个域对象。因为 DTO 没有行为,因此它不能从域对象提取数据。这是对的,因为如果让 DTO 不知道域对象,您就可以在不同的上下文中重用 DTO。同样,您不希望域对象知道 DTO,因为这可能意味着更改 DTO 将要求更改域逻辑中的代码,这将导致大量维护任务。

最佳的解决方案是使用Assembler模式 [Fowler03],该模式可以用业务对象创建 DTO 或者相反。Assembler是Mapper模式的专门实例,在Patterns of Enterprise Application Architecture [Fowler03] 中也提到过它。

图 3:使用Assembler将数据加载到DTO中

Assembler 的关键特征是 DTO 和域对象不相互依赖。这就消除了这两种对象的相互影响。不利方面是 Assembler 同时依赖于 DTO 和域对象。对这些类的任何更改都可能导致必须更改 Assembler 类。

测试考虑事项

DTO 是简单对象,它不应该包含需要测试的任何业务逻辑。但是,您确实需要测试每个 DTO 的数据聚合。每个 DTO 可能需要测试,也可能不需要,这取决于您的序列化机制。如果序列化是框架的一部分,则只需要测试一个 DTO。如果不是这样,请使用一般的反射机制,这样就不需要测试每个 DTO 的序列化。

DTO 还对远程函数的可测试性有好处。通过使远程方法的结果能够在对象实例中使用,可以轻松地将此数据传递到测试模块,或将其与所需结果进行比较。

安全考虑事项

理想情况下,应该先筛选和验证从不可靠的来源获得的数据(如来自 Web 页的用户输入),然后将其置于 DTO 中。通过这样做,就可以认为 DTO 中的数据是相对安全的,从而简化了将来与 DTO 的交互。

接收 DTO 的进程和关联用户的安全凭据也是值得注意的。DTO 通常包含从许多不同来源聚集在一起的大量信息。您是否已授权 DTO 的所有用户访问 DTO 所包含的所有信息?确保用户已得到授权的最佳方法是仅使用用户安全凭据所允许的特定数据填充 DTO。努力避免让 DTO 负责自己的安全性。这将增加 DTO 对其他类的依赖数,这意味着必须将这些类部署到使用 DTO 的所有节点。这还会将安全性功能分散到更多类中,从而增大了安全风险,并对灵活性和可维护性产生负面影响。

结果上下文

Data Transfer Object 具有下列优缺点:

优点

"

减少了远程调用次数。通过在单个远程调用中传输更多的数据,应用程序可以减少远程调用次数。

"

提高了性能。远程调用可以使应用程序的运行速度大大降低。减少调用次数是提高性能的最佳方法之一。在大多数方案中,传输大量数据的远程调用所用的时间与仅传输少量数据的调用所用的时间几乎相等。

"

隐藏内部情况。在单个调用中来回传递更多的数据,还可以更有效地将远程应用程序的内部情况隐藏在粗粒度接口的背后。这就是使用 RemoteFacade 模式 [Fowler03] 的主要原因。

"

发现业务对象。在一些情况下,定义 DTO 有助于发现有意义的业务对象。在创建用作 DTO 的自定义类时,您通常会注意到作为一组凝聚性信息而显示给用户或另一个系统的元素分组。通常,这些分组用作描述应用程序所处理的业务域的对象的有用原型。

"

可测试性。将所有参数封装到可序列化对象中可以提高可测试性。例如,可以从 XML 文件中读取 DTO,并调用远程函数以测试它们。同样,可以轻松地将结果再序列化为 XML 格式,并将 XML 文档与所需结果进行比较,而不必创建冗长的比较脚本。

缺点

"

可能需要太多的类。如果选择了使用强类型的 DTO,则可能必须为每个远程方法创建一个(如果考虑返回值,则为两个)DTO。即使在粗粒度接口中,这也可能导致大量的类。编写如此数量的类的代码并管理这些类会是很困难的。使用自动代码生成可以在一定程度上缓解此问题。

"

增加计算量。如果将服务器上的一种数据格式转换为可以跨网络传输的字节流,并在客户端应用程序内转换回对象格式,可以带来相当大的开销。通常,需要将来自多个源的数据聚合到服务器上的单个 DTO 中。要提高通过网络进行远程调用的效率,必须在任一端执行其他计算,才能聚合和串行化信息。

"

增加编码工作量。可以用一行代码完成将参数传递到方法的操作。使用 DTO 要求实例化新对象,并为每个参数调用 setters 和 getters。编写此代码可能是很乏味的。

相关模式

"

Remote Facade。Data Transfer Object模式通常与粗粒度Remote Facade联合使用,以减少远程调用次数。

"

Mapper[Fowler03]。Mapper是用域对象中的数据元素加载 DTO 的建议技术。

"

Value Object。一些资料将 Data Transfer Object称为 Value Object。此用法不再被认为是正确的。有关详细信息,请参阅Patterns of Enterprise Application Architecture[Fowler03]。

java dto是什么_java中DTO与DAO的问题相关推荐

  1. java可以多重继承吗_Java中的多重继承与组合vs继承

    java可以多重继承吗 有时我写了几篇有关Java继承,接口和组成的文章. 在这篇文章中,我们将研究多重继承,然后了解组成优于继承的好处. Java中的多重继承 多重继承是创建具有多个超类的单个类的能 ...

  2. java中有没有栈_Java中堆和栈有什么区别

    stack 和 heep 都是内存的一部分stack 空间小,速度比较快, 用来放对象的引用heep 大,一般所有创建的对象都放在这里.栈(stack):是一个先进后出的数据结构,通常用于保存方法(函 ...

  3. java 基本类型 引用_java中 引用类型 和 基本类型 有何区别?

    栈与堆都是Java用来在Ram中存放数据的地方.与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆. Java的堆是一个运行时数据区,类的(对象从中分配空间.这些对象通过new.newa ...

  4. java构造器详解_Java中关于构造器的使用详解

    这篇文章主要介绍了Java构造器使用方法及注意事项的相关资料,这里举例说明如何使用构造器及需要注意的地方,需要的朋友可以参考下 Java构造器使用方法及注意事项 超类的构造器在子类的构造器运行之前运行 ...

  5. java读取文件 路径_Java中的获取文件的物理绝对路径,和读取文件

    获取文件的绝对路径,读取该文件 一.文件目录打印图 下面的文件目录图,是项目中文件的位置信息:下面的例子是按照这个图来演示的. . |-- java | |-- ibard | | |-- demo1 ...

  6. java多线程 线程安全_Java中的线程安全

    java多线程 线程安全 Thread Safety in Java is a very important topic. Java provides multi-threaded environme ...

  7. java 异常处理发生异常_Java中的异常处理

    java 异常处理发生异常 Exception Handling in Java is a very interesting topic. Exception is an error event th ...

  8. java string 对象地址_Java中String对象的存储位置(学习笔记)

    packagetest.string.equal;public classMain {/*** 创建了三个对象,"helloworld对象创建在常量池中",每次new String ...

  9. java 定义整数数组_JAVA中数组的正确定义方法是什么?

    数组是有序数据的集合,数组中的每个元素具有相同的数组名和下标来唯一地确定数组中的元素. §5.1一维数组 一.一维数组的定义 type arrayName[]; 其中类型(type)可以为Java中任 ...

最新文章

  1. 闪耀澳网 跨界新品泸州老窖国窖1573澳网冠军版发布
  2. Pci设备驱动:设备枚举
  3. SPOJ.TLE - Time Limit Exceeded(DP 高维前缀和)
  4. 命令行导出数据mysql数据库_MySQL命令行导出数据库
  5. Java学习笔记----线程
  6. 镜像体积减小_docker镜像从1.16G优化到22M
  7. C#静态类,静态构造函数,静态变量
  8. Python可视化库Matplotlib的使用
  9. java记秒,Java程序获取自Java时代开始以来的秒数
  10. 提取身份证信息的自定义函数
  11. Java防御目录穿越漏洞方法_WinRAR目录穿越漏洞复现及防御
  12. 数据库服务器信息填写,数据库服务器是怎么填写
  13. 复选框java_java之swing实现复选框的方法
  14. 万能五笔输入法弹窗_万能五笔输入法广告屏蔽的方法
  15. 【锐捷无线】加密配置
  16. 88页《Redis学习文档》,从入门到精通,看这一篇就足够
  17. 软件测试人员每天的工作日常
  18. java中根号2怎么表示_根号2的计算方法(Java实现)
  19. MIDI通信协议-数据字节:找到中央C(音名:C4)
  20. 联想微型计算机m910q6,一台比较完美的黑苹果小主机 联想M910Q折腾记 opencore EFI分享...

热门文章

  1. html设置及格不及格良好优秀,Excel用IF函数算优秀良好及格不及格各等级成绩
  2. 我对Android的认识
  3. 将一个大数转为科学计数法并保留两位小数打印
  4. 【RK3399 Android】天马MIPI屏幕TM080TDGP01,启动参数dts配置调试。
  5. python 大数据量绘图_Matplotlib绘图遇到时间刻度就犯难?现在,一次性告诉你四种方法...
  6. 软考 信息系统项目管理师证书会过期吗?高项证书有效期
  7. vb实现 心理学实验 IAT内隐联想测试
  8. 基于链路聚合提升带宽:winserver2016对接华为S5720交换机
  9. Anaconda闪退的问题
  10. 路由器怎么访问服务器共享文件夹,如何让你的文件在路由器内网共享