本文探讨以下内容:编程
要理解Entity和VO,须要先理解两个概念:「状态」和「标识」!咱们先来聊聊「状态」!网络
你们确定都在淘宝买过东西吧!在淘宝购买商品后,会有一个订单,记录了你购买的商品信息、价格、店铺信息、还有一个特别重要的信息,就是订单状态。经过这个订单状态,咱们能够知道咱们的购物流程如今进行到哪一步了。若是你犹豫了好久才下定决心购买了一件心仪已久的商品,你是否是很在乎订单状态?时不时要刷新一下页面,看看订单状态是否显示已送达了?架构
开发过系统的都知道,通常订单状态都是使用一个字段来表示的,好比status,不一样的状态就是给status赋不一样的值。可是这个status就是「订单状态」吗?难道状态就是一个字段?!并发
Order{ product location seller buyer status ... }
你有没有想过,当咱们说「状态」的时候,咱们实际上指的是什么?编程语言
咱们在不少场景下会用到「状态」这个词,好比:分布式
以「你今天状态不错」这句为例,若是状态就是一个字段!那么,「你今天状态不错」就是status=1?!「你今天状态不行」就是status=0?!很明显,这不合理!动画
若是「状态」不是简单的一个字段的话,那么「状态」究竟是什么呢?设计
其实在架构风格:你真的懂REST吗?已经提过了!文中对REST的解释,有这么一句:一个由网页组成的网络(一个虚拟状态机),用户经过选择连接在应用中前进(状态迁移),致使下一个页面(应用的下一个状态的表述)被转移给用户,而且呈现给他们,以便他们来使用。指针
结合上面的几个场景,你有没有发现,「状态」实际上表示的是「目标对象在当前时刻所呈现出的内容」!在软件系统中经过一个字段来表示状态只是一种简化手段!对象
如无特殊说明,下面所提到的「状态」指的是「目标对象在当前时刻所呈现出的内容」,而不是指状态字段
既然「状态」表示的是「当前时刻所呈现出的内容」!那么说明了「状态」是个快照/瞬态!也就是说,「目标对象」有多个「状态」,「当前状态」只是「目标对象」众多「状态」中的一个!
你们应该玩过定格动画吧?就像下面这样(下图截自《大侦探福尔摩斯2:诡影游戏》):
图中的小册子就是「目标对象」,册子的每一页就是「状态」,当前展现出来的那一页就是「当前状态」!
在理解了什么是「状态」之后,咱们就能够来初步区分Entity和VO了:
如今,问题又来了,对于VO来讲,由于「状态」是不可变的,咱们就能够用其「状态」来表示VO!可是对于Entity来讲,由于有多个「状态」,且「状态」是可变的,那咱们如何来表示呢?以上面的Order为例,假设同一个买家在同一个卖家那里买了两个一样的商品,那两个订单里的信息都是同样的,可是它是两个不一样的订单,咱们如何区分这两个订单呢?
如今就轮到下一个主角登场了:「标识」!
说到「标识」,咱们最早想到的是编程语言中的「引用」或「指针」!好比下面的代码:
Order orderA = new Order("productA",...); Order orderB = new Order("productA",...); orderA.productName = "productB";
这解决了「区分相同状态的不一样Entity」的问题,可是没有解决Entity有多个状态的问题。由于「标识」指向的是目标对象的当前状态。并且,不少编程语言中有个很大的问题,就是不区分「标识」和「状态」!什么意思呢?
假设咱们在看一部电影,当咱们开始观看时,就是这部电影生命周期的开始,观看结束就是这部电影生命周期的结束,在这段时间里,电影的画面(状态)一帧帧的呈如今咱们面前,咱们能够经过播放、快进、后退、暂停改变电影的状态,每一个状态都是相互独立的,相似这样:
随着时间的改变,咱们能获取到电影的不一样状态,每一个状态是相互独立的。可是实际上咱们的代码逻辑像下面这样:
var movie1 = new Movie(); movie1.setCurrentFrame("第三帧"); var currentMovie = movie1 movie1.setCurrentFrame("第四帧"); currentMovie // 仍是第三帧吗?
电影播放到第三帧,咱们用一个变量currentMovie保存了电影的当前状态(第三帧),可是后面电影播放第四帧了,currentMovie也就变成了第四帧的状态了。
语言中的这种「标识」(我称为「隐式标识」)还有另一个问题,就是没法跨系统。好比,在分布式系统中,须要保证两个系统中的对象是同一个对象,这种「隐式标识」是作不到的。
因此「隐式标识」并不能知足咱们的需求。咱们须要「显示标识」,「显示标识」在现实中很常见:
在上面购物的列子中,就至关于给Order一个惟一标识,好比一个惟一的订单号:
Order{ orderNo // 显示标识 product location seller buyer status ... }
给定订单号之后,不管订单的状态如何变化,只要订单号不变,那么它就是同一个订单。
因此,「标识」是另外一个区分Entity和VO的关键点:
注意标识并不必定只是一个字段,多是多个字段的组合,这须要根据不一样的业务逻辑来肯定。好比在一个学校系统里,能够经过学年+班级+学号来标识一个学生。
理解了标识和状态,咱们就能够来定义Entity和VO了:
如今咱们知道了什么是Entity,什么是VO,那么咱们如何在系统中识别哪些对象是Entity,哪些对象又是VO呢?
一个对象是表示成Entity仍是VO,取决于系统的关注点。
咱们还以淘宝购物为例,假设你在某家店铺买了个商品,质量很好。过了一段时间后,你想再买一个,可是你记不得是哪家店了,因而你从已完成的订单列表中点击商品想进去再次购买。可是你点进去后发现,商品下架了。
这是由于「商品」在「订单系统」中是个VO,而在「商品管理系统」中是Entity!其实很好理解:
在「商品管理系统」中,商品能够这样表示:
Product { id // 商品标识 name desc status ... }
而在「订单系统」中,订单是个Entity,商品是个VO,能够这么表示:
Order{ orderNo // 订单标识 product:Product status ... } Product { id // 这里不是标识,只是状态 name desc status ... }
注意这里的id并非标识,这里的id实际上退化成了状态的一部分,保留这个id是为了和「商品管理系统」进行交互,经过id从商品管理系统中查询商品。固然还有其它方式,例如保存「商品管理系统」中该商品的历史URL。
本文从对「状态」和「标识」的理解开始,一步步来解释什么是Entity和VO,以及如何在系统中识别Entity和VO。后面将进一步讨论Entity与VO的关系,以及与其它组件的关系,例如DTO,Service,Resporitory,DAO等