DDD 学习笔记 | 极客时间 | 手把手教你落地 DDD
🏆 原文:12|代码实现(下):怎样更加“面向对象”?
使用表格与业务方确认需求简单明了
属性 | 创建 | 修改 | 备注 |
---|
组织 ID | - | ? | 系统自动生成,不可修改 |
租户 | 是 | ? | 操作不能跨租户,因此不可更改,但需要作为后续操作的输入参数 |
更新、取消操作可以放在 Handler 领域服务中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| package chapter12.unjuanable.domain.orgmng.org;
// imports...
@Component
public class OrgHandler {
private final CommonValidator commonValidator;
private final OrgNameValidator nameValidator;
private final OrgLeaderValidator leaderValidator;
private CancelOrgValidator cancelValidator;
public OrgHandler(CommonValidator commonValidator
, OrgNameValidator nameValidator
, OrgLeaderValidator leaderValidator
, CancelOrgValidator cancelValidator) {
// 为依赖注入赋值...
}
public void updateBasic(Org org, String newName
, Long newLeader, Long userId) {
updateName(org, newName);
updateLeader(org, newLeader);
updateAuditInfo(org, userId);
}
public void cancel(Org org, Long userId) {
cancelValidator.cancelledOrgShouldNotHasEmp(org.getTenant()
, org.getId());
cancelValidator.OnlyEffectiveOrgCanBeCancelled(org);
org.setStatus(OrgStatus.CANCELLED);
updateAuditInfo(org, userId);
}
private void updateLeader(Org org, Long newLeader) {
if (newLeader != null && !newLeader.equals(org.getLeader())) {
leaderValidator.leaderShouldBeEffective(org.getTenant()
, newLeader);
org.setLeader(newLeader);
}
}
private void updateName(Org org, String newName) {
if (newName != null && !newName.equals(org.getName())) {
nameValidator.orgNameShouldNotEmpty(newName);
nameValidator.nameShouldNotDuplicatedInSameSuperior(
org.getTenant(), org.getSuperior(), newName);
org.setName(newName);
}
}
private void updateAuditInfo(Org org, Long userId) {
// 设置最后修改人和时间
}
}
|
提高应用 API 的封装性
- 最小接口原则,目的是减小模块间的耦合,提高程序的可维护性
- 要为每个功能编写单独的参数 DTO,只包含必要的参数
添加组织的 JSON 参数修改前
1
2
3
4
5
6
7
8
9
10
11
12
13
| {
"createdAt": "2022-10-05T06:49:39.659Z",
"createdBy": 0,
"id": 0,
"lastUpdatedAt": "2022-10-05T06:49:39.659Z",
"lastUpdatedBy": 0,
"leader": 0,
"name": "string",
"orgType": "string",
"status": "string",
"superior": 0,
"tenant": 0
}
|
添加组织的 JSON 参数修改后
1
2
3
4
5
6
7
| {
"leader": 0,
"name": "string",
"orgType": "string",
"superior": 0,
"tenant": 0
}
|
通过“表意接口”提高封装性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| package chapter12.unjuanable.domain.orgmng.org;
// imports ...
@Component
public class OrgHandler {
//...
public void cancel(Org org, Long userId) {
cancelValidator.OrgToBeCancelledShouldNotHasEmp(
org.getTenantId(), org.getId());
cancelValidator.OrgToBeCancelledShouldBeEffective(org);
org.setStatus(OrgStatus.CANCELLED); // 直接为 Status 赋值
updateAuditInfo(org, userId);
}
// ...
}
|
其中,org.setStatus(OrgStatus.CANCELLED) 直接将组织的状态设置成了“已撤销”。从面向对象的角度来看,更好的做法是 Org 类提供一个 cancel() 方法,像下面这样:
1
2
3
4
5
6
7
8
9
| package chapter12.unjuanable.domain.orgmng.org;
public class Org {
//Org 自己管理自己的状态
public void cancel() {
this.status = OrgStatus.CANCELLED;
}
}
|
这样,Org 类就可以自己管理自己的状态,OrgHandler 就不必了解 Org 内部状态的转换细节,只需告诉 Org 需要撤销就可以了,像下面这样。
1
2
3
4
5
6
7
8
9
10
11
12
13
| package chapter12.unjuanable.domain.orgmng.org;
@Component
public class OrgHandler {
public void cancel(Org org, Long userId) {
cancelValidator.OrgToBeCancelledShouldNotHasEmp(
org.getTenant(), org.getId());
cancelValidator.OrgToBeCancelledShouldBeEffective(org);
org.cancel(); // 只需告诉 Org 要进行撤销,但不必了解 Org 内部细节
updateAuditInfo(org, userId);
}
}
|
类似的,我们看一下“只有有效的组织才能被撤销”这条规则的实现代码。
1
2
3
4
5
6
7
8
9
10
11
12
| package chapter12.unjuanable.domain.orgmng.org.validator;
@Component
public class CancelOrgValidator {
// 只有有效的组织才能被撤销
public void OnlyEffectiveOrgCanBeCancelled(Org org) {
//直接访问了状态属性
if (!org.getStatus().equals(OrgStatus.EFFECTIVE)) {
throw new BusinessException("该组织不是有效状态,不能撤销!");
}
}
}
|
我们看到 CancelOrgValidator 直接访问了 Org 的状态,判断是否为有效组织。这实际上又是重构中的一种坏味道,叫做特性依恋(Feature Envy)。Status 这个特性是属于 Org 类的,而 CancelOrgValidator 要通过访问这个特性来实现自己的逻辑,所以 CancelOrgValidator “依恋”了 Org 的特性。这是对象封装性被破坏的征兆。
解决方法是将这段判断逻辑移动到 Org 类内部,重构后的 Org 类是这样的。
1
2
3
4
5
6
7
| package chapter12.unjuanable.domain.orgmng.org;
public class Org {
public boolean isEffective() {
return status.equals(OrgStatus.EFFECTIVE);
}
}
|
这样,CancelOrgValidator 就可以不依赖 Org 的内部状态了。
1
2
3
4
5
6
7
8
9
10
11
12
13
| package chapter12.unjuanable.domain.orgmng.org.validator;
@Component
public class CancelOrgValidator {
// 只有有效的组织才能被撤销
public void OnlyEffectiveOrgCanBeCancelled(Org org) {
//不再依赖 Org 的内部状态
if (!org.isEffective()) {
throw new BusinessException("该组织不是有效状态,不能撤销!");
}
}
}
|
Org.cancel() 和 Org.isEffective() 既提高了对象的封装性,也表达了这个功能的业务含义,因此也是表意接口模式的一种应用。