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、定义一个基础的实体类,该类中包含用户的一些基本信息:

java 复制代码
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,用于在不同层之间传输用户数据,同时隐藏敏感信息,比如密码:

java 复制代码
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,专门用于前端展示,进一步精简信息,只包含前端需要显示的数据:

java 复制代码
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之间的转换:

java 复制代码
// 假设我们有一个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

相关推荐
Elieal18 小时前
Spring MVC 全局异常处理实战
spring·mvc·状态模式
Elieal19 小时前
统一 JSON 格式,JacksonObjectMapper 定制 Spring Boot JSON 转换规则
spring boot·json·状态模式
前端不太难19 小时前
HarmonyOS PC 应用,先做文档模型
华为·状态模式·harmonyos
前端不太难19 小时前
HarmonyOS 走向 PC,应用模型正在重构
重构·状态模式·harmonyos
进击的小头2 天前
行为型模式:状态模式——嵌入式状态管理的优雅解决方案
c语言·状态模式
洋不写bug2 天前
JavaEE基础,计算机是如何工作的
java·java-ee·状态模式
前端不太难2 天前
HarmonyOS 游戏里的“假异步”,为什么会卡
游戏·状态模式·harmonyos
阿珊和她的猫2 天前
React 路由:构建单页面应用的导航系统
前端·react.js·状态模式
zihan03213 天前
element-plus, el-table 表头按照指定字段升降序的功能实现
前端·vue.js·状态模式
钦拆大仁3 天前
Java设计模式-状态模式
java·设计模式·状态模式