18|值对象(上):到底什么是值对象?
例一:员工状态
- 深入了解
- 翻译
- 解释
- 总结
领域驱动设计(DDD)中的重要概念——值对象,是本文的重点。作者通过详细讲解值对象的应用,包括员工状态和时间段的建模,并对值对象进行了分类,帮助读者更好地理解值对象的概念。通过具体的代码示例和领域模型图,本文为领域驱动设计提供了实用的指导。同时,文章提出了思考题,引发读者对于值对象的深入思考和探讨。值对象的重要性和应用在领域驱动设计中的价值得到了充分展现。
《手把手教你落地 DDD》,新⼈⾸单¥59
全部留言(11)
- 最新
- 精选
- escray置顶Java 代码写的少,居然不知道 enum 里面也可以有方法。按照类似的思路,是不是类似于状态机的代码都可以放在枚举类里面? 员工状态这样的对象(enum)是没有”生命“的。 时间段类也是第一次见到,原谅我书读得少,其实这段代码解决之前困扰我的一个问题,也是和历史版本相关的。 这里对于代码的重构是通过”设计“来完成的。 实体是靠独立于其他属性的标识 identity 来确定同一性 identity 的。 有单独标识,理论上可以改变的对象,叫做实体 Entity,是一个”东西“; 没有单独标识,并且不可以改变的对象,叫做值对象 Value Object,是一个”值“。 对于思考题, 1. 日期中的年、月、日三个属性,如果单独来看,拆散了”日期“这个对象的原始意义,只有当三个值都存在的时候,”日期“才有意义,时间也类似。 2. 货币如果要写成代码话,需要把币种种类也加进来,然后可能还需要增加一个”转换汇率“的属性值?同种货币的数值与数值相加,然后保留币种。
作者回复: 笔记记得很好,继续努力! 状态机代码可以放在Enum。 汇率转换,可能放在单独的“转换率”对象里比较好。关于货币的其他思路没问题。
2023-01-30归属地:北京1 - aoe思考题 1. 在日期的定义为“以年月日确定某一天”的前提下,它在概念上不能再拆分,符合“原子值对象”的定义。例如:生日、节假日、工资到账时间这些都能定位到具体的日期,年月日缺一不可。 2. 代码如下 public enum Currency { GOLD_COIN("金币"), GEEK_COIN("极客币"), ; private String name; Currency(String name) { this.name = name; } } import java.math.BigDecimal; public class Money { private final BigDecimal value; private final Currency currency; public Money(BigDecimal value, Currency currency) { this.value = value; this.currency = currency; } public Money add(Money other) { if (other == null) { throw new IllegalArgumentException("货币不能为空"); } if (this.currency != other.currency) { throw new IllegalArgumentException("货币类型不一致,请转换成相同货币类型后进行计算"); } return new Money(this.value.add(other.value), this.currency); } public BigDecimal value() { return value; } } import org.junit.jupiter.api.Test; import java.math.BigDecimal; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; class MoneyTest { @Test void returns_3_when_1_plus_2(){ Money one = new Money(new BigDecimal("1"), Currency.GOLD_COIN); Money two = new Money(new BigDecimal("2"), Currency.GOLD_COIN); BigDecimal result = one.add(two).value(); assertThat(result).isEqualTo(new BigDecimal("3")); } } 读后感 原来 Java 中的 String 是值对象! 快照也是值对象 简单理解:不可变的对象就是值对象 开始日期、结束日期封装在了“时间段对象”以后模型也跟着变了,确实是保持了“模型与代码一致”
作者回复: 第1题,正如你说的,日期本质上是一个不可分割的时点。年、月、日只是一种表达方式,是表象;比如说,理论上也可以用从公元1年1月1日到现在的总日期数来表达日期。 第2题,代码写得不错,可贵的是还有单元测试。可以在Money对象里直接有一个equals()方法,这样就不用通过value()来判断相等了。
2023-01-17归属地:浙江27 - Ice开始时间和结束时间修改成值对象之后,与之对应的数据库结构是否需要调整呢? 还是说在持久化层再做一次映射转换
作者回复: 数据库一般不调整,在仓库转换
2023-02-06归属地:四川5 - 一剑老师,我有个问题:EmpStatus的方法是Public的,所以Emp的EmpStatus属性是为了控制状态变更不被外部直接调用所以设成了protected么?但是在实际项目里,状态应该是要用Public对外公开的吧?但是一旦公开,就可能会被人绕过聚合根而直接调用emp.status.becomeRegular()了,这个怎么解决?
作者回复: EmpStatus是值对象,因此,他的becomeRegular()方法只是返回一个新的值对象,而不会改变任何现有的东西。所以emp.status.becomeRegular()并不会改变emp的任何状态。
2024-01-10归属地:江西 - Geek_967502钟老师您好,我有一个疑惑,领域模型中没有每个领域实体的具体字段,是如何识别的值对象,会不会识别的不够准确。按照理解应该是每个阶段的输出结果都能对下一个阶段起到支撑,但值对象这一章,感觉没办法通过之前的成果完整的分析值对象
作者回复: 领域模型是逐步演进的,遇到的时候识别就可以了。
2023-05-31归属地:北京 - benhuang文中提到JAVA date对象是可变的带了很多问题,具体是什么问题
作者回复: 比如说线程不安全
2023-03-17归属地:广西 - 6点无痛早起学习的和尚思考题和一些问题: 问题:1. 所以本篇文章举例的员工状态、时间段 员工状态:原子值对象+依附于实体的值对象 时间段:复合值对象+独立的值对象 所以多种多样的值对象分类之间并不是完全独立不相交 思考题: public class Money { private Long value; private Currency currency; private Money(Long value, Currency currency) { this.value = value; this.currency = currency; } public static Money of(long value, Currency currency) { return new Money(value, currency); } public static Money addTwoMoney(Money money1, Money money2) { // 这里引入货币计算规则,把 2 个货币全部转成人民币,然后进行计算,再 new Money(value,人民币)返回 return new Money(0L, Currency.CNY); } @Getter @AllArgsConstructor public enum Currency { CNY("CNY"); //省略其他货币... private String name; } }
作者回复: addTwoMoney 可以改成非静态的: public Money add(Money other) { ....... //校验是同一种货币 return new Money(this.value + other.value, this.currency); }
2023-02-15归属地:北京 - Geek_ab5b86老师,类似员工实体中的身份证号idNum,我认为也是原子值对象,是不是说明也是不能修改的,实体内部不能加set方法,只能通过构造器传入值对象属性重新构造员工实体呢?
作者回复: 是的
2023-01-16归属地:上海 - Geek_1e04e7分别表达年月日的值对象也是有的,职责不一样
作者回复: 日期是时间轴上一个点,本来无所谓再分属性。年月日只是一种表示方法,还可以有其他表示法,不影响日期不可分的本质。
2023-01-16归属地:广东 - 阿昕1.从含义上来看,年月日组合在一起是日期的一种固有格式,是一个整体,所以是原子值对象; 2.货币相加,需要先校验币种是否统一,是否需要转换;
作者回复: 没错
2023-01-15归属地:浙江