Java中各种O(PO,BO,DTO,VO等) 是不是人为增加系统复杂度?

在Java和其他编程语言的开发过程中,经常会用到几个以"O"结尾的缩写,比如PO,BO,DTO,VO等等,O在这里是Object的缩写,不同的O代表了不同的数据类型,很多时候这些O看起来都是差不多的,干的事情好像也只是一个简单的封装,那么搞出这么多O出来是不是人为增加了系统的复杂度呢?

各种O都是干什么的?

想要搞清楚标题中的问题,我们首先得了解这些O都是什么东西?这里给大家介绍几种常见的O:

  1. PO (Persistent Object) - 持久化对象。 持久化对象通常对应数据库中的一个表,主要用于表示数据库中存储的数据。PO中的属性通常和数据表的列一一对应,用于ORM(对象关系映射)框架中,如Hibernate,JPA等。
  2. BO (Business Object) - 业务对象。 业务对象主要封装了业务逻辑。它可以包含多个PO,或者是一个PO的扩展,增加了业务处理的逻辑。BO通常在业务层被使用,用于实现业务操作,比如计算、决策等。
  3. VO (Value Object) - 值对象。 值对象是一种用于传输数据的简单对象,它通常不包含业务逻辑,只包含数据属性和get/set方法。值对象主要用于业务层与表示层之间的数据传递,它的数据可能是由多个PO组合而成。
  4. DTO (Data Transfer Object) - 数据传输对象。 数据传输对象类似于VO,它也是用于层与层之间的数据传递。DTO通常用于远程通信,比如Web服务之间的数据传递。DTO通常不包含任何业务逻辑,只是用于在不同层次或不同系统之间传输数据。

有时候我们还会看到DO、POJO等概念,它们又是什么呢?

  1. DO (Domain Object) - 领域对象。 领域对象是指在问题领域内被定义的对象,它可以包含数据和行为,并且通常代表现实世界中的实体。在DDD(领域驱动设计)中,领域对象是核心概念,用于封装业务逻辑和规则。这里需要注意DO和BO的区别,虽然都是搞业务逻辑,DO通常是业务领域中单一实体的抽象,它关注于单个业务实体的属性和行为;而BO则通常涉及到业务流程的实现,可能会协调多个DO来完成一个业务操作。
  2. POJO (Plain Old Java Object) - 简单老式Java对象。 POJO是指没有遵循特定Java对象模型、约定或框架(如EJB)的简单Java对象。POJO通常用于表示数据结构,它们的实例化和使用不依赖于特定的容器或框架。

为什么要划分各种O?

在软件开发中划分不同的O主要是为了实现关注点分离(Separation of Concerns,SoC),提高代码的可维护性、可读性和可扩展性。

关注点分离的典型案例:MVC模式。

下面展开列举了一些划分这些对象的原因:

  1. 明确职责:通过将不同的职责分配给不同的对象,可以使每个对象都有明确的职责,这样代码更容易理解和维护。
  2. 减少耦合:不同层次之间通过定义清晰的接口(如特定的对象)交互,减少了直接的依赖关系,降低了耦合度。
  3. 抽象层次:通过定义不同的对象,可以在不同的抽象层次上操作,比如在数据层处理PO,在业务层处理BO,这样可以在合适的层次上做出决策。
    • 灵活性:当系统需要变更时,由于职责和层次的清晰划分,更容易做出局部的修改而不影响到整个系统。不同的对象可能针对性能有不同的优化,例如PO可能被优化以提高数据库操作的性能。
    • 安全性:通过使用不同的对象,可以控制敏感数据的暴露。例如,可以在DTO中排除一些不应该传输到前端的敏感信息。
    • 测试性:分离的对象使得单元测试变得更加容易,因为可以针对每个对象进行独立的测试。
  1. 交互清晰:在不同的系统组件或层次之间传递数据时,清晰的对象定义可以让数据交互更加清晰,减少数据传递中的错误。

总之,通过划分各种"O"对象,开发者可以更好地组织代码,将复杂系统分解为更小、更易于管理的部分,同时也有助于团队成员之间的沟通和协作。这种划分在设计模式和软件工程实践中是一种常见且有效的方法。

OO不分的惨痛经历

说个实际的惨痛经验。

很多时候我会感觉这些O之间存在很多重复的代码,比如重复的属性定义、简单的方法封装,DRY(Don't Repeat Yourself)原则不是说让大家避免重复嘛,所以我也曾经尝试在程序中统一它们。

但是总有一些O之间存在或多或少的差异,比如:

  • 这个O需要一个A属性,仅用于内部状态管理,不会暴露到外部,其它O都不需要。
  • 还有这个接口需要返回一个B属性,其它接口都不需要。

这时候,你怎么办?如果使用同一个类型,那就得加上这些属性,尽管它们在某些时候用不到。根据你的选择,你可能在所有的地方都给这个属性赋值,也可能仅在业务需要的时候给他们赋值。

看个实际的例子:在一个复杂的电商系统中,商品的管理可能涉及到库存管理、价格策略、促销信息等多个方面。

arduino 复制代码
// 商品类
public class Product {
    private Long id; // 来自商品表
    private String name; // 来自商品表
    private double price; // 来自商品表,传输时需要特殊格式
    private int stock; // 来自库存表,仅在下单判断中需要,展示层不需要
    private String promotionInfo; // 来自促销表,展示层需要

    // 构造器、getter和setter方法省略
}

但是这却带来了很大的危害:

  • 调用接口的同学会问,这个属性什么时候会有值,什么时候会没值?
  • 优化的同学会问,计算这个属性的值会影响性能,能删掉吗?
  • 交接的同学会问,这个属性是干什么用的,为什么不给他赋值?

总之会增加了大量的沟通成本与维护难度。一旦这样做了,后边就会特别别扭,改不完,根本改不完。

在软件工程化的今天,各类O的设计看似增加了复杂度,但是实际上是对系统模块化、职责划分以及实际应用场景的合理抽象和封装,有助于提高软件质量和团队协作效率。

老老实实写吧,不同的O就是不同的东西,它们不是重复的,只是在代码上看着像,就像人有四肢,动物也有四肢,但是它们不能共用,否则出来的就是四不像。

图片来源:ozhanozturk.com/2018/01/28/...

当然如果只是一个很简单的程序或者一次性的程序,我们确实没必要划分这么多的O出来,直接在接口方法中访问数据库也不是不可以的。

前端中O的使用

虽然各种O一般活跃在各种后端程序中,但是前端也不乏O的身影,只是没有后端那么形式化。

以下是一些可能在前端开发中遇到的以"O"结尾的数据对象:

  1. VO (View Object) - 视图对象。在前端框架中,VO可以代表专门为视图层定制的数据对象。这些对象通常是从后端接口获取的数据经过加工或格式化后,用于在界面上显示的对象。
  2. DTO (Data Transfer Object) - 数据传输对象。虽然DTO通常用于后端服务间的数据传输,但在前端中也可以用来表示从后端接口获取的数据结构。前端的DTO通常是指通过Ajax或Fetch API从服务器获取的原始数据结构。
  3. VMO (ViewModel Object) - 视图模型对象。在MVVM(Model-View-ViewModel)架构中,VMO可以代表视图模型对象,它是模型和视图之间的连接器。在Vue.js中,Vue实例本身就可以被看作是一个VMO,因为它包含了数据和行为,同时也是视图的反映。
  4. SO (State Object) - 状态对象 尽管不是标准的术语,但在使用如Vuex这样的状态管理库时,SO可以用来指代代表应用状态的对象。这些状态对象通常包含了应用的核心数据,如用户信息、应用设置等。

在实际的Vue开发过程中,开发者可能不会严格区分这些概念,而是更多地关注于组件的状态、属性(props)、事件和生命周期。组件内部的数据通常以数据属性(data)的形式存在,而组件间的数据传递通常使用属性(props)和事件(emits)。在处理与后端的数据交互时,开发者可能会定义一些专门的对象来适应后端的接口,但是这些都不是Vue框架强制的概念或规则。


简单地说,这些"O"其实就是帮我们把代码写得更清晰、更有条理,虽然一开始看着很麻烦,但时间一长,你会发现这样做能省下不少力气。就像我们的衣柜,虽然分类放好衣服需要点时间,但每天早上起来挑衣服的时候,不就轻松多了吗?

所以,别被这些专业术语吓到,它们其实就是帮我们把事情做得更好的小工具。每个"O"都有它的用处和场景,我们要做的,就是搞清楚什么时候戴哪顶帽子,这样生活和编程都能变得轻松愉快。记住,合适的工具用在合适的地方,能让你事半功倍!

关注萤火架构,加速技术提升!

相关推荐
骄马之死29 分钟前
SpringMVC + SpringBoot 核心知识点总结
java·spring boot·后端
zhengfei61141 分钟前
第3章 Agent 类型分类与设计模式
设计模式
GoGeekBaird1 小时前
Anthropic技能"(Skills)的经验分享
后端
王码码20352 小时前
多台服务器怎么统一看状态?Beszel 轻量监控,搭起来不费事
运维·服务器·后端·安全·阿里云·接口·web
刀法如飞2 小时前
一文搞懂DDD 领域驱动设计思想原理
设计模式·架构·代码规范
郑洁文2 小时前
基于Spring Boot的流浪动物救助网站
java·spring boot·后端·毕设·流浪动物救助
指令集梦境3 小时前
Cursor + Spring Boot实战:从零写一个RESTful API
spring boot·后端·restful
码云之上4 小时前
聊聊如何设计一个高效、稳定的 Node.js 接入层
前端·后端·node.js
折哥的程序人生 · 物流技术专研4 小时前
Java 23 种设计模式:从踩坑到精通 | 原型模式 —— 克隆对象,深拷贝与浅拷贝的坑你踩过吗?
java·设计模式·架构·原型模式·单一职责原则