当前位置: 首页 > news >正文

DTO和VO的区别及使用场景详解

随着互联网的发展,前后端分离的开发模式越来越流行。在前后端数据交互过程中,为了保证数据的安全性和效率,通常会采用 DTO 和 VO 来封装数据。本篇博客将详细介绍 DTO 和 VO 的区别以及使用场景。 

大家可能会有个疑问,既然DTO是展示层与服务层之间传递数据的对象,为什么还需要一个VO呢?对!对于绝大部分的应用场景来说,DTO和VO的属性值基本是一致的,而且他们通常都是POJO,因此没必要多此一举,但不要忘记这是实现层面的思维,对于设计层面来说,概念上还是应该存在VO和DTO,因为两者有着本质的区别,DTO代表服务层需要接收的数据和返回的数据,而VO代表展示层需要显示的数据。

用一个例子来说明可能会比较容易理解:例如服务层有一个getUser的方法返回一个系统用户,其中有一个属性是gender(性别),对于服务层来说,它只从语义上定义:1-男性,2-女性,0-未指定,而对于展示层来说,它可能需要用“帅哥”代表男性,用“美女”代表女性,用“秘密”代表未指定。说到这里,可能你还会反驳,在服务层直接就返回“帅哥美女”不就行了吗?

对于大部分应用来说,这不是问题,但设想一下,如果需求允许客户可以定制风格,而不同风格对于“性别”的表现方式不一样,又或者这个服务同时供多个客户端使用(不同门户),而不同的客户端对于表现层的要求有所不同,那么,问题就来了。再者,回到设计层面上分析,从职责单一原则来看,服务层只负责业务,与具体的表现形式无关,因此,它返回的DTO,不应该出现与表现形式的耦合。

一、什么是VO?什么是DTO?

DTO(Data Transfer Object)和 VO(Value Object)都是一种设计模式,用于封装数据和提供服务。它们的主要区别在于:

  • VO(View Object):视图对象,专门用于前端展示层,专注于表示某个具体的值或对象的对象,包含业务逻辑;VO的作用是将一组数据以适合特定用户界面(UI)的形式封装起来,确保数据的呈现既符合设计要求也满足用户体验标准。
    • 例如我们有一个电子商务网站,其中产品详情页需要显示产品的名称、价格、库存、图片等信息。VO会将这些信息以最优化的方式组织起来,供前端展示。比如,前端可能需要将产品分类显示为“热销商品”、“新品推荐”或“特价优惠”,VO会根据不同的展示需求,以适当的形式提供数据。例如,将从后端接收到的“男性”标签在客户端1上显示为“帅哥”,而在客户端2上显示为“靓仔”。
  • DTO(Data Transfer Object):数据传输对象,侧重于传输数据的对象,不包含业务逻辑;主要在展示层与服务层之间充当媒介,负责数据的标准化传输,确保数据在不同系统或组件间的准确无误传递。
    • 例如当展示层需要向服务层请求数据时,例如查询“男性”类别的产品,它会将“男性”这一概念封装进DTO中,以标准化格式发送请求。服务层接收到这个DTO后,理解为需要查询“男性”类别相关的产品数据,处理后同样以DTO的形式返回,其中可能包括所有“男性”类别的产品信息。这样,无论前端如何展示(如“帅哥”或“靓仔”),后端只需处理统一的“男性”类别,实现了前后端的解耦。

两者的关系:

  • 当DTO与VO一一对应时,DTO等于视图模型,与VO属性值一致
  • 当一个DTO需适配多个VO时,DTO不等于视图模型,与VO不等价

二、使用场景

DTO

✨优点:可以避免数据的重复查询和传输,提高程序的性能和效率。减少代码的复杂度和维护难度,方便代码的开发和维护。因为DTO可以封装业务逻辑和数据格式,减少数据处理的复杂度。,增加数据的安全性。
✨缺点:可能增加开发初期的工作量,尤其是在需要创建和维护多个DTO的情况下,同时过度使用可能引入不必要的复杂性。

VO

✨优点:精确适配前端展示需求,促进前后端解耦,提高代码质量和可读性,因为VO专注于展示层面,避免了业务逻辑的混杂。
✨缺点:在处理大量数据或高并发场景下可能会影响前端性能,而且在多团队协作环境下,对VO的管理和同步可能变得较为复杂。

  • 跨系统数据标准化传输:选DTO,确保数据的一致性和格式标准化,减少数据处理的复杂度。
  • 前后端数据交换与解耦:用DTO,它能匹配前端需求,促进数据交换。
  • 保护敏感信息:DTO可以在数据传输过程中过滤敏感信息,而VO可以在前端展示层剔除非必要信息。
  • 前端展示优化:VO最适合,它针对显示优化,让页面加载更快更准确。
  • 简化代码:DTO和VO都能让代码更模块化,易于理解和维护。

三、在Java中使用DTO和VO的示例代码

1、定义一个基础的实体类,该类中包含用户的一些基本信息:

public class User {private Long id;private String username;private String password;private String email;private String phone;private boolean active;// 构造器,getters 和 setters 省略...
}

2、创建一个DTO,用于在不同层之间传输用户数据,同时隐藏敏感信息,比如密码:

public class UserDTO {private Long id;private String username;private String email;private String phone;private boolean active;public UserDTO(User user) {this.id = user.getId();this.username = user.getUsername();this.email = user.getEmail();this.phone = user.getPhone();this.active = user.isActive();}// getters 和 setters 省略...
}

3、定义一个VO,专门用于前端展示,进一步精简信息,只包含前端需要显示的数据:
 

public class UserVO {private String username;private String contactInfo;public UserVO(UserDTO userDTO) {this.username = userDTO.getUsername();this.contactInfo = userDTO.getEmail() + " | " + userDTO.getPhone();}// getters 和 setters 省略...
}

UserDTO从User实体类中获取数据,但不包含敏感信息,如密码。UserDTO 类,用于封装从数据库中查询出来的用户数据,并将其转换为前端需要的格式。该类只包含必要的属性(id、username、email),可以避免不必要的数据传输,提高程序的性能和效率。UserVO进一步从UserDTO中提取数据,只保留前端展示所需的用户名和联系方式。

以下是UserDTO和UserVO的转换示例,实际应用中,构造器和方法可能需要更详细的错误检查和边界处理,这里为了简洁起见,省略了这部分细节。通常会使用框架提供的工具或库(如ModelMapper、AutoMapper)来自动完成实体、DTO和VO之间的转换:

// 假设我们有一个User实例
User user = new User();
// ...设置user的属性...// 将User转换为UserDTO
UserDTO userDTO = new UserDTO(user);// 将UserDTO转换为UserVO
UserVO userVO = new UserVO(userDTO);

可以看到,UserDTO 包含了用户信息的全部属性,用于在前后端之间传输数据。由于 DTO 只包含数据属性,不包含任何业务逻辑,因此可以避免数据的重复查询和传输,提高程序的性能和效率。

需要注意的是,DTO 和 VO 只是一种设计模式,具体的实现方式可以根据具体的业务需求和技术架构来选择。在实际开发中,可以根据需要使用 DTO、VO 或者其他方案来封装数据。


参考链接:https://blog.csdn.net/qq_46207024/article/details/134726394

                https://zhuanlan.zhihu.com/p/79968039

相关文章:

DTO和VO的区别及使用场景详解

随着互联网的发展,前后端分离的开发模式越来越流行。在前后端数据交互过程中,为了保证数据的安全性和效率,通常会采用 DTO 和 VO 来封装数据。本篇博客将详细介绍 DTO 和 VO 的区别以及使用场景。 大家可能会有个疑问,既然DTO是展…...

百度在下一盘大棋

这两天世界互联网大会在乌镇又召开了。 我看到一条新闻,今年世界互联网大会乌镇峰会发布“2024 年度中国互联网企业创新发展十大典型案例”,百度文心智能体平台入选。 这个智能体平台我最近也有所关注,接下来我就来讲讲它。 百度在下一盘大棋…...

第十六届蓝桥杯模拟赛第二期题解—Java

第十六届蓝桥杯模拟赛/校赛第二期个人题解,有错误的地方欢迎各位大佬指正 问题一(填空题) 【问题描述】 如果一个数 p 是个质数,同时又是整数 a 的约数,则 p 称为 a 的一个质因数。 请问, 2024 的最大的质因数是多少? …...

驱动开发笔记:关于3588GPIO

1.概要 2.内容 1.3588GPIO 关于RK3588的GPIO(General-Purpose Input/Output,通用输入输出引脚),以下是一些关键信息和操作指南: 一、GPIO基本概念 定义:GPIO是嵌入式系统中常见的通信接口,…...

【RK3588 Linux 5.x 内核编程】-内核线程与Mutex

内核线程与Mutex 文章目录 内核线程与Mutex1、Mutex介绍1.1 竞争条件1.2 Mutex特性2、Linux内核中的Mutex2.1 初始化Mutex2.1.1 静态方式初始化2.1.2 动态方式初始化2.2 互斥锁获取2.3 互斥锁释放3、Mutex使用示例4、驱动验证在前面的文章中,介绍了如何Linux内核中的线程,但是…...

【0342】分配并初始化 Proc Signal 共享内存 (1)

1. Proc Signal (procsignal)共享内存 Postgres内核在启动postmaster守护进程时候, 会通过函数 ProcSignalShmemInit() 去为 Proc Signal 分配并初始化指定大小的共享内存空间。整个调用链路如下。 (gdb) bt #0 ProcSignalShmemInit () at procsignal.c:118 #1 0x000000000…...

管家婆财贸ERP BR035.回款利润明细表

最低适用版本: 财贸系列 23.5 插件简要功能说明: 报表统计销售单/销售退货单/销售发票回款情况更多细节描述见下方详细文档插件操作视频: 进销存类定制插件--回款利润明细表 插件详细功能文档: 1. 应用中心增加报表【回款利润明细表】 a. b. 查询条件: ⅰ. 日期区间:…...

数据库MYSQL——表的设计

文章目录 前言三大范式:几种实体间的关系:一对一关系:一对多关系:多对多关系: 前言 之前的博客中我们讲解的是关于数据库的增删改查与约束的基本操作, 是在已经创建数据库,表之上的操作。 在实…...

netstat -tuln | grep 27017(显示所有监听状态的 TCP 和 UDP 端口,并且以数字形式显示地址和端口号)

文章目录 1. 确定占用端口的进程使用 lsof 命令使用 fuser 命令 2. 结束占用端口的进程3. 修改 MongoDB 配置文件4. 检查 MongoDB 日志文件5. 重新启动 MongoDB 服务6. 检查 MongoDB 服务状态总结 [rootlocalhost etc]# netstat -tuln | grep 27017 tcp 0 0 127.0.…...

非线性控制器设计原理

非线性控制器设计原理 非线性控制器设计旨在解决非线性系统的控制问题,克服传统线性控制器在处理非线性现象(如饱和、死区、耦合、时变性等)时的不足。其核心在于利用非线性数学工具和设计方法,使控制系统在非线性条件下具备良好…...

MySQL数据库6——SQL优化

一.SQL优化 1.插入优化 优化1:批量插入 insert into 表名 values(记录1),(记录2),……;优化2:手动提交事务 start transaction; insert into 表名 values(记录1),(记录2); insert into 表名 values(记录1),(记录2); …… commit;优化3:主键顺…...

IDEA配置本地maven

因为idea和maven是没有直接关系的。所以使用idea创建maven工程之前需要将本地的maven配置到idea环境中,这样才可以在idea中创建maven工程。配置方法如下: 1.1 配置本地maven 第一步:关闭当前工程,回到idea主界面找到customize--…...

学习日记_20241123_聚类方法(高斯混合模型)续

前言 提醒: 文章内容为方便作者自己后日复习与查阅而进行的书写与发布,其中引用内容都会使用链接表明出处(如有侵权问题,请及时联系)。 其中内容多为一次书写,缺少检查与订正,如有问题或其他拓展…...

SpringMVC——简介及入门

SpringMVC简介 看到SpringMVC这个名字,我们会发现其中包含Spring,那么SpringMVC和Spring之间有怎样的关系呢? SpringMVC隶属于Spring,是Spring技术中的一部分。 那么SpringMVC是用来做什么的呢? 回想web阶段&#x…...

文件操作完成后,为什么要关闭文件

原因包括: 释放系统资源:打开文件时,操作系统会分配资源,如文件描述符或句柄,用于管理文件访问。如果文件保持打开状态,这些资源就不会被释放,可能导致资源耗尽。 确保数据完整性:写…...

vue3+echarts+ant design vue实现进度环形图

1、代码 <div> <!-- 目标环形图 --><div id"main" class"chart_box"> </div><div class"text_target">目标</div> </div>// 目标环形图 const onEcharts () > {// 基于准备好的dom&#xff0c;初…...

使用argo workflow 实现springboot 项目的CI、CD

文章目录 基础镜像制作基础镜像设置镜像源并安装工具git下载和安装 Maven设置环境变量设置工作目录默认命令最终dockerfile 制作ci argo workflow 模版volumeClaimTemplatestemplatesvolumes完整workflow文件 制作cd argo workflow 模版Workflow 结构Templates 定义创建 Kubern…...

C++知识点总结(58):序列型动态规划

动态规划Ⅰ 一、基础1. 意义2. 序列 dp 解法 二、例题1. 最大子段和2. 删数最大子段和&#xff08;数据强度&#xff1a;pro max&#xff09;3. 最长上升子序列&#xff08;数据强度&#xff1a;pro max&#xff09;4. 3 或 5 的倍数序列5. 数码约数序列 一、基础 1. 意义 动…...

go interface(接口)使用

在 Go 语言中&#xff0c;接口&#xff08;interface&#xff09;是一种抽象类型&#xff0c;它定义了一组方法&#xff0c;但是不实现这些方法。接口指定了一个对象的行为&#xff0c;而不关心对象的具体实现。接口使得代码更加灵活和可扩展。 定义接口 接口使用 type 关键字…...

【docker】docker commit 命令 将当前容器的状态保存为一个新的镜像

在Docker容器中安装了许多软件&#xff0c;并希望将当前容器的状态保存为一个新的镜像&#xff0c;可以使用docker commit命令来创建一个新的镜像。以下是如何操作的步骤&#xff1a; 找到容器ID或名称&#xff1a; 首先&#xff0c;需要找到想要保存的容器的ID或名称。可以使用…...

vLLM-v0.11.0快速入门:用OpenAI接口调用本地大模型,5分钟出结果

vLLM-v0.11.0快速入门&#xff1a;用OpenAI接口调用本地大模型&#xff0c;5分钟出结果 1. 为什么选择vLLM&#xff1f; 1.1 什么是vLLM&#xff1f; vLLM是伯克利大学LMSYS组织开源的高性能大语言模型推理框架。它通过创新的内存管理技术&#xff0c;显著提升了模型推理的效…...

实战应用:使用快马平台为vmware17部署生成企业级健康检查与配置方案

在实际的企业IT环境中&#xff0c;部署VMware vSphere 17&#xff08;以下简称VMware 17&#xff09;这类虚拟化平台往往不是简单的安装过程&#xff0c;而是需要综合考虑硬件兼容性、系统配置、安全策略等多方面因素。为了确保部署过程的顺利和后续运行的稳定&#xff0c;我们…...

2016-2025年地级市链长制数据

在产业链现代化与协同治理进程中&#xff0c;“链长制”作为一项关键的制度创新&#xff0c;为破解产业链条松散、协同不足等问题提供了重要抓手&#xff0c;其政策效果与影响机制成为当前学术研究与政策制定的焦点议题。周钰丁、田思远在研究中指出&#xff0c;产业链“链长制…...

万象视界灵坛快速部署:GitLab CI流水线自动触发镜像构建与K8s滚动更新

万象视界灵坛快速部署&#xff1a;GitLab CI流水线自动触发镜像构建与K8s滚动更新 1. 项目概述 万象视界灵坛&#xff08;Omni-Vision Sanctuary&#xff09;是一款基于OpenAI CLIP技术的高级多模态智能感知平台。该平台通过创新的像素风格界面&#xff0c;将复杂的语义对齐过…...

AI大模型时代:微店商品数据API如何重构反向海淘决策

在AI大模型时代&#xff0c;微店商品数据API凭借覆盖下沉市场、小众货源、私域供给的独特优势&#xff0c;成为重构反向海淘决策的核心支撑&#xff0c;将传统“人工经验判断”升级为“数据采集→AI分析→自动决策→反馈优化”的全链路数据驱动模式&#xff0c;大幅提升选品精准…...

Android USB串口通信终极指南:智能家居物联网项目实战

Android USB串口通信终极指南&#xff1a;智能家居物联网项目实战 【免费下载链接】usb-serial-for-android Android USB host serial driver library for CDC, FTDI, Arduino and other devices. 项目地址: https://gitcode.com/gh_mirrors/us/usb-serial-for-android …...

Metro性能监控终极指南:如何快速分析和优化React Native打包构建过程

Metro性能监控终极指南&#xff1a;如何快速分析和优化React Native打包构建过程 【免费下载链接】metro &#x1f687; The JavaScript bundler for React Native 项目地址: https://gitcode.com/gh_mirrors/me/metro Metro作为React Native的官方JavaScript打包工具&a…...

Java 无人图书借阅系统设计与完整源码实现

以下是一个基于Java的无人图书借阅系统的设计与完整源码实现方案&#xff0c;涵盖系统架构、核心模块、数据库设计、关键代码实现及部署建议&#xff1a;一、系统架构设计1. 分层架构表现层&#xff1a;用户端&#xff1a;微信小程序&#xff08;UniApp开发&#xff09; H5页面…...

比特币钱包密码与助记词恢复工具:从入门到精通

比特币钱包密码与助记词恢复工具&#xff1a;从入门到精通 【免费下载链接】btcrecover An open source Bitcoin wallet password and seed recovery tool designed for the case where you already know most of your password/seed, but need assistance in trying different…...

进程间通信(IPC):原理、场景与选型

在操作系统的世界里&#xff0c;进程是程序运行的基本单元&#xff0c;每个进程都拥有独立的内存空间和资源&#xff0c;彼此之间相互隔离&#xff0c;无法直接访问对方的数据。这种隔离机制保证了系统的稳定性&#xff0c;避免进程间相互干扰&#xff0c;但也带来了一个问题&a…...