VO的一些个人认识

刚开始的时候Entity和VO很容易混淆,分不太清楚,不过在实际使用之后有了一些个人的想法,先不管对错,贴出来探讨一下。

banq老师说VO是不能更改的,只能整体替换,这个我现在还没有理解。我自己从另一个角度的理解:
VO关乎于对象的粒度,VO来源于Entity,用VO来将Entity的一些职责分解出来,造成更加细粒度的对象来控制Entity不至于膨胀成为胖对象。
比如:contract对象就会分解出balance这个VO来,将金额控制的逻辑存放在balance中,contract对外封装调用而已。

实体和VO关系实际可以看成是主次之分,每次关注哪个Which或者Who等PPT时,只能关注一个,这句话怎么理解呢?就是一旦你进入战术纠结细节时,就只能选择一个,客观上你已经选择一个,比如你向南出发,其实你已经在东西南北四个中选择了南方向。

既然你已经选择了一个实体作为关注(包括其唯一标识),那么它就是主要关注对象,围绕这个实体其他高聚合属性就沦为附属次要地位值对象了。

结果变为楼主所说:可以控制实体粒度,造成更加细粒度的对象来控制Entity不至于膨胀成为胖对象。

也许我因为经验的欠缺,所以没有考虑的那么深,只是得到了一个可以控制细粒度的结果,看来还需要慢慢体会,再次感谢banq老师。

VO的不可改变性,决定了它的整体性,这是一种边界思维。或者更好的说法就是“实体状态”(PS),实体是连续状态的综合体,但我们只关注其离散的状态,所以就需要“状态迁移”。状态是整体的,刚好与值对象对应。

至于逻辑内聚,是边界体现,与不变性是无关的。(不会因为“不变”,所以就“内聚”吧)

不变性是因为离散关注。

“VO是不能更改的,只能整体替换”、“VO的不可改变性”……
这类结论,需当仔细研究
书本上的意思是:强烈建议值对象不可变。值对象不可变为的是共享。为什么值对象涉及到共享,实体不涉及共享概念?因为值对象没有自己独特身份“ID”,这样就可以节省系统开销——共享。既然共享,那么值对象就不能随意修改,因为要考虑引用这个共享值对象的其他对象的“感受”。
但是,值对象自打创建就不能修改吗?

啊,吃饭先。下回再说。

值对象自打创建就不能修改吗?我们举一个例子。公交车线路作为实体,而停靠站作为值对象。在这一现实业务模型中,停靠站不是永远不变的,公交公司会根据现实情况做调整。
值对象肯定是会变的。不需要把“值对象不可变”作为金科玉律,并为此产生冲突而烦恼。如果某个值对象会变,那么,这个值对象共享出来的时候不是把真实的“自我”共享出来,而是提供一个副本给其他某个引用对象。这样,这个引用对象对于得到的值对象副本的任何修改都不会传递给其他引用对象。

值对象到底是变还是不可变?这个问题还是由领域模型来解答,也就是由需求决定它变还是不变。

2011年06月02日 14:44 "@showerxp"的内容
这个值对象共享出来的时候不是把真实的“自我”共享出来,而是提供一个副本给其他某个引用对象。 ...

恩,这个方向我也曾考虑过,不过我注意到一点,这是两个方向,效果是一样的。但是理解不同,可能产生的效率也不同,代码如下:

副本流:


class A{
```
public String getVO(){
return VO.deepCopy();
}
}
//外界
VO vo = a.getVO();
vo.setField1(vo.getField1()+
"0");
a.setVO(vo);

不变流:


class A{
```
public String getVO(){
return vo;//注意,该方式VO是没有set方法。
}
}
//外界
VO oldVO = a.getVO();
String field1 = oldVO.getField1();
//取出旧VO中的值
String newField1 = field1+
"0";//这里可以理解为领域逻辑
VO newVO = new VO(newField1);
//构建新状态,新的VO只能创建出来,新旧没任何改变,实现状态迁移的同时,也满足共享。
a.setVO(newVO);
//状态迁移

比较这两种情况,两种都是在setVO实现状态迁移(即之前不会有任何改变)。这两种的区别是,第一种是保留set方法,但每次访问都需要建立副本,变更时,只与副本相关,与本体无关;第二种是去掉set方法,每个VO都是如同1、2、"123"、12.5、true等成为“值”的存在,变更时,是把VO中的值获取出来,赋给一个变量,在变量上作变化,变化后,作为VO的构造构建一个新的VO,用作状态迁移(状态迁移则是迁移前后的两个状态,即只有两个VO,只读取不迁移则不需要创建新状态)。

In my opinion,把第二种方法所有变量都用一个带set的VO封装起来的话,就与第一种同理了。第一种里的副本已经不是VO了,“值”本身不可变,而是变量可变,值变化是概念的彻底变化,如1不是1了,用2代替1,1代替2,于是有2+2=1,这是值变化带来的,也就是说只要概念不发生变化,值是不存在变化的。
总的来说,我的观点是:值带有可以改变自身概念的方法就不是值了。

PS:发现这问题可以深入探讨,欢迎新观点、新概念出现。

值对象不可变我也是一直存有疑问,现在非常认同showerxp的说法,不教条,从实际出发进行取舍,不生搬硬套。

2011年06月03日 13:32 "@pye"的内容
不教条,从实际出发进行取舍,不生搬硬套。 ...

教不教条,因人而已。我何尝不是实际出发呢?(我字典里从来没有教条,我个人觉得教条一词的出现,才是教条的开始,把别人知识融到自己的知识当中,这压根儿没有教条意思,是人们喜欢排他,才用教条将其划开)

DDD一书也说出不变性的来源和目的。没有细读和实践,很难体会其中的优劣。当中是别人长期实践总结出来的,我们看到其“是什么”同时,也要看其“为什么”,这样教条一词就无影无踪了。

当中还有就是他老人家在DDD中定义值对象的含义就是那样,我也没办法,他DDD中的buzzword,我们还是不要随便歪曲,大不了,自己另立一个就是了。

灵活适应是好事,但也别因为为了适应来调整概念,毕竟那是别人经过推敲的严密的一整套概念。随便改,只会带来BUG。

PS:值对象不变概念,在DDD一书中有谈到,建议细读。我个人认为,那是funtional的思维。

理没说多少,话却不少

2011年06月04日 15:19 "@pye"的内容
理没说多少,话却不少 ...

各自见解,各有道理。而此话一出容易产生不和谐。

领域驱动设计的理念横空出世,我觉得从本质上讲就是提出了两个命题:1、软件设计应当回归传统面向对象思想,将软件设计重点放在需要解决问题本身——领域上。2、软件设计应当考虑软件实施,使得软件设计向软件实施平滑过渡。
第一个命题其实在我《飞跃迷雾,把OO看清楚》文中已经探讨过,但是没有直接把领域这一时髦词汇引进。第二个命题原本想另开一个主题来具体阐述一下领域驱动设计中那些强调的固定模式(这里的“模式”是指软件设计过程中需当基本遵守的规约、方法等),比如实体、值对象、仓库等概念出现的理论意义。这些概念的本源或者源于命题一,或者源于命题二。当我们弄清楚这些固定模式的理论意义时,或许就能解开诸如“为什么区分实体和值对象”’、、“为什么值对象提倡不可变”、“为什么要仓库统一来执行关于数据库的crud操作”等问题。
不过,本人很懒,提笔忘字,所以一直没有下定决心写命题二的帖子,只是在论坛中,看到相关讨论就零散的说一些。
说这么多歪论,大家姑且听之,不取之处权当胡说。抑或留心,说不定另有一番见解。

无冒犯之意,
SpeedVan的帖子看了不少,初学者的角度,颠来倒去的像哲学,很搞,呵呵

2011年06月06日 08:09 "@pye"的内容
无冒犯之意,
SpeedVan的帖子看了不少,初学者的角度,颠来倒去的像哲学,很搞,呵呵 ...

冒犯不冒犯我也不怎么在意,只是看起来很不解——明明上面已经有不变和可变的代码了,也浅说了一下自己的见解。怎么何来不说理呢?若果你是说原不变概念劣于可变概念,完全可以说出自己的见解,而那句看起来不怎么舒服的话则完全可以不要了。

哲学,这东西,因人而异。但这正是每个人对世界的理解,话题以外的适量解析,是有助于交流。例如这里“教条”理解,你认为我仍然教条则随你,我稍微解析后,也就不作深讨了,毕竟这与帖子的主题无关。

2011年06月04日 15:24 "@showerxp"的内容
所以一直没有下定决心写命题二的帖子,只是在论坛中,看到相关讨论就零散的说一些。 ...

你说的命题二正是DDD的本源,可以说若果要细说解析各种理论,可以翻几天的书了。若果你能引起命题二的讨论和交流,可能会引起几个月的交流热潮吧,呵呵。

我没有认定可变好于不可变,或是反之。
我这里举例分解的contract和balance仅仅只是主从关系的角度出发而已
每一次发生commitAmount之后,balance肯定是会变化的,到现在为止我实在想不出这里把VO定义为imutable的理由,实际上我也没有什么可靠的理论依据。我只是觉得经常会更改,或者更改完以后不对主体发生本质影响的VO也是可靠的。

不可变的情况在我看来,应该是下面的这个例子:
product和specification,商品和他的规格应该是不可变的,变化后应该视为新的商品,不是原来的。

我还是继续前进并分裂着。

这就有点不懂你所讲了,你所讲的balance到底是变量还是值对象呢?
变量------值(值对象)
balance = 1346;
balance = new Balance(1346);