#原创分享# DDD领域建模之【实体】进阶第一篇【值对象】

      接着上期的内容我们继续开展DDD的讨论,作为DDD构建的基础【实体】,是其最基本的构成。计算机软件用于无限趋近模式进行模拟我们现时社会的种种活动,故一切皆可建模(在当下的一段时期内,这条规则有一定的正确性),不知道【实体】建模(OOP)是什么时候开始提倡的,也许是从当年的C++语言开始的,从而开创一个伟大的编程时代,在我们讲解【DDD】内容之前,【实体】建模【OOP思想】已经被无数人讲解与阐述,这不在累述了。

       在这里基于【实体】概念之下,要提出一个粒度更细的物件【值对象】,他与【实体】比较,在粒度上更为精细、内容上更为内敛的【对象建模】划分,如果把【实体】看作分子,【值对象 】可以看作是原子(不一定是最恰当的,但是基本可以说明一些问题的实质),【值对象】与【实体】之间有着千丝万缕的关系,在一定条件下【值对象】可以作为【实体】参与到【界限上下文】中。在一定程度上甚至可以互相置换使用,那么【值对象】具备那些特征呢?为什么会存在【值对象】这样的概念呢?他的存在解决了什么问题呢等等,接下的来的篇幅逐一为大家解答,这也是我在【DDD】学习从理论到实践过程中的一些心得,

       尽管我们讨厌给名词下定义,下定义本身这意味者对事物在我们脑海中产生束缚,限制的大家想象事物的无限可能,但是在这里还是要采用教科书模式一样的定义,【值对象】大体而言:用于度量与描述事物最小单位。在实际的操作中,特别是后期在DDD场景下提到的【聚合】的场景中,会大量使用【值对象】进行聚合。就一般而言,【值对象】含有如下一些特定的属性:

      1、他是描述事物本身的最小单位,这个理解就比较难了,什么是最小单位呢?我这边可以举个例子来(不一定恰当但是能说明问题本身),我们在软件开发中常常定义一些计量单位 时间、长度、货币等,如果仅仅有单位, 缺少了数字这个标量,那么他的存在不具备任何业务意义,例如 米、厘米,千克等,所以我们必须用 数字+某个特定的计量单位  组装在一起,才能清晰的表述某一个事实或者业务本身具体的业务含义,如 1厘米、1小时等。上述两个元素缺一不可,那么这个对象基本实现了成为【值对象】的充分条件。    在引申一些,这样的业务模型在建模的同时 ,我们还可以内部涵盖 单位之间的换算 如 1米=100厘米。  大家按照这个思路 可以大概了解【值对象】的一些概念了把,在实际的【DDD】实践中,“构建事物本身的小单位”:其中隐藏了潜在的条件,这些条件在显式的告诉你:对于某一对象而言,只有确定不变的属性,才能构成表示该事物的【值对象】,用这些属性的组合可以精准的描述一个实际的【实体】模型。例如 如果我们针对 集装箱 而言,如果用【值对象】的概念进行建模,那么那些属性才是箱子不变的属性呢?这些数据一旦装配在一起出现,我们立刻就可以确定他是一个具体且唯一的【实体】或者【实体】的引用?  如  箱型,箱号、出场日期、出厂厂家 ,伴随箱子的诞生,这些已经作为箱子不可分隔的不部分,这些值都应该 用 final 来修饰,标识这些属性是无法被修改的,除了被销毁以外,在没有其他渠道可以修改这些属性。

      2、【值对象】的不可变性,一旦我们在程序中构建【值对象】,那么他就成为具体某个【实体】的代名词,代码体现则更加明显:是所有的属性都是 final 来修饰的。大家都知道 final 类型的属性,都是不可变参数。在这里还需要进一步明确,【值对象】在被我们初始化后,变没有任何方法可修改,或者不提供任何修改内部属性的方法,反射都是不用许的,所以用final修饰的值对象是最为稳妥了,我在编码的过程中,一旦简单 有 final 修饰符修饰的变量,总有一种莫名的安全感。另外 【值对象】任何时候它是一个整体,~~~~~~在实际的编码中:我常常在多个对象构建的【聚合】中使用,毕竟这个世界是复杂的,任何一个事物的构成都不是单一物体,所以大部分而言,都是N个对象相互协助,形成一个更加完备的业务模型或者其他什么,这时,每个对象在不同的角度来观察,会有所侧重,这边给我使用【值对象】提供了优厚的条件,例如 实体模型 订单。在购物车模式下。与在结账模式下,都是订单,但是关注点不同,在购物车模式下 订单 更加关注商品本身的属性,而在结账模式下,订单更加关注的是支付相关的实体对象。。。。。。 活学活用,在一些特定的场合下,我们可以打破【值对象】表示单一事物的规则,而是把几个或者N个事物合体为一个更加完整的big【值对象】。。。。更加方便、快捷的对外提供稳定,不变的服务。

      3、【值对象】的可替换性,这个理解比较字面一些,就是该对象是原子性存在,不可分割。他是具体【实体】对象的最小集合定义,如果【值对象】发生改变,那么只有一个办法:整个【值对象】被替换了。

      4、【值对象】的相等性判断,这一点需要大家在编程的时候注意,两个一样的【值对象】 判断的结果应该是与预期一致的,例如 1米=100厘米 类似这样的场景,这样就需要我们在编写程序的时候重写 hasCode 与 equesl 方法,一开始采用【DDD】实践时,【值对象】的hasCode 与 equesl 方法,特别的别扭,但是需要认真的对待,特对是针对 含有list、set 等属性时? 需要我们重写这些对象的相等方法。

       另外构成【值对象】的其他对象也 应该都是【值对象】。 这里在 我强烈推荐 guava 的Objects 自带的一些方法,在属性判断是否一致上方便不少,JDK8 之后 也引入了较为简单的 判等方法,极大的方便了我们编码,特别是这对集合中提出的 不可变集合,需要在实际的代码中活学活用。

     5、【值对象】的无副作用行为方法,具体体现为方法都是安全、无副作用。之前我们已经了解到【值对象】属性的不可修改性。在对象内部的属性对外暴露的方法都应当是 readOnly 。 不可否认,在某一些场景下,【值对象】存在一些值改变的场景,遇到这样的场景如何支持呢?想来用【OOP】的思想已经有了答案:直接上代码说明:

public class ValueObj {

    private final int value; // 度量
    private final String unit; // 单位

    public ValueObj(int value, String unit) {
        this.value = value;
        this.unit = unit;
    }

    // ... 一些读方法 以及   toString  equals  hashCode

    // 无副作用的方法
    // clone 也是一种解决方案 不拘一格
    public ValueObj changgeMe(int value) {
        if (this.value == value)return this;
        return new ValueObj(value, this.unit);
    }
}

      如上述代码展示,所有的改变都是在创造新的对象,【值对象】本身并会有任何的变化,貌似这样的一个平常、普通的属性, 需要大家自己的体会与琢磨其中的内涵......,大多数情况下,【实体】对象尽可能采用【值对象】来代替,通过他来装配怕【聚合】时,他固有的不变性为我们带来许多编程上的方便。

        当我们掌握了【值对象】的基本概念之后,是否你也想尝试在实际的编码过程中,开始使用【值对象】呢? 一开始可能【值对象】的定义有一定的偏差,但是没关系的,至少我们在【实体】建模时候,有了更加清晰的目标与可遵循的道路。随之时间的推移,逐步会精准的定义【值对象】本身。

     回忆当年我开始接触【DDD】,开始尝试考虑【值对象】的一些概念构建程序时,是很蹩脚的,生搬硬套,还不断的指导下级兄弟如何去使用,想来多少有些汗颜,随着时间的推移,【值对象】在具体的场景使用的越来越得心应手了,他的不变性让我在编程的过程中可以做更少的职责假设,在【聚合】中更加灵活的装配而满足不同场景(界限上下文)的需要,这点 类似Hibernate,但是又有本质的区别。 我们要尝试采用【值对象】来表示一些标准的的实体类型概念,Java中的枚举类型也算是一种最简单的【值对象】吧。

       通常而言,我们会为【值对象】构建构造函数,在构造函数中完成初始化,但是在实际操作时,从持久化层还原这些属性可能有先后顺序,没关系的,我先通过 构造函数初始化第一波之后,在通过 setter 方法构建起他的属性,需要注意的时这些属性尽可以初始化一次后,便不再有初始化的机会了,从而保证值对象的不可变性,代码如下展示:

// ...... 代码片段
void setterXXXXX(XXXX xxxxx){
   if(this.xxxx == null)
        this.xxxx = xxxxx;
}

        上述仅仅是一种编程的上技巧,还是那句话~~ 程序时灵活的~~~ 用到熟练时,一切都简单了。上述的方法 展示了一种自我委派性,也是我比较推荐的一种【值对象】初始化的一种方法。

         还有一点需要强调的时【值对象】在随宿主对象clone时,可采用 浅复制 或者 深复制模式,这些都需要通过构造函数完成自身的复制,我一般不会考虑太多复制的模式,理由很简单:他是不可变的,N个不同的对象完全可以共享一个实例,除非。你要考虑替换值对象本身的。

 

 

 

转载于:https://my.oschina.net/qfhxj/blog/3081821

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值