最近看了一下关于代码架构的一些书,想要融会贯通还需要实践的沉淀,虽然我们公司比较注重代码简洁之道的人还是偏少,但突破桎梏,方能成为至尊强者,废话说到这~
一、充血模式(Rich Domain Model)
定义:
充血模式是一种强调将业务逻辑封装在领域对象中的设计模式。领域对象不仅仅存储数据,还负责实现与自身相关的业务行为。
特点:
- 数据与行为封装:对象既有数据(属性),也有行为(方法),业务逻辑被封装在对象内部。
- 面向对象的特性:充分体现了面向对象设计的封装性和高内聚特性。
- 对象自治:对象对自己的状态负责,任何修改都通过对象自身的方法完成。
- 依赖外部服务少:尽可能减少对外部服务的依赖,业务逻辑主要集中在领域对象内部。
优势:
- 高内聚:领域对象对自己的状态和行为负责,逻辑分散性较低。
- 可维护性:业务逻辑集中在领域对象中,方便理解和修改。
- 体现领域专家语言:代码与领域语言一致,更容易被领域专家理解。
劣势:
- 复杂性高:需要设计合理的领域模型,前期的分析和开发成本较高。
- 职责混乱的风险:如果不合理划分对象职责,可能导致领域对象过于复杂,成为“大对象”。
示例代码:
以“银行账户”为例:
public class BankAccount {
private String accountNumber;
private BigDecimal balance;
public BankAccount(String accountNumber, BigDecimal initialBalance) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
public void deposit(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Deposit amount must be positive");
}
this.balance = this.balance.add(amount);
}
public void withdraw(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Withdraw amount must be positive");
}
if (this.balance.compareTo(amount) < 0) {
throw new IllegalStateException("Insufficient balance");
}
this.balance = this.balance.subtract(amount);
}
public BigDecimal getBalance() {
return balance;
}
}
特点:deposit 和 withdraw 方法封装了存款和取款的业务逻辑,直接体现业务规则。
二、贫血模式(Anemic Domain Model)
定义:
贫血模式是一种将领域对象设计为仅仅包含数据(属性),不包含业务逻辑的模式。业务逻辑被放在服务层或其他独立组件中。
特点:
- 数据与行为分离:领域对象只负责存储数据,不包含业务逻辑。
- 领域对象变得简单:领域对象更像是 DTO,主要是数据的载体。
- 业务逻辑集中在服务层:业务逻辑由独立的服务类负责实现。
优势:
- 简单易用:领域对象设计简单,理解和开发成本较低。
- 灵活性高:服务层集中处理业务逻辑,容易调整和扩展。
- 符合传统分层架构:与很多开发者习惯的分层架构(如 Controller-Service-DAO)兼容。
劣势:
- 低内聚:数据和逻辑分散在不同地方,增加维护难度。
- 逻辑集中导致代码膨胀:服务类容易变得臃肿。
- 弱化领域模型:领域模型不能很好地表达业务语义。
示例代码:
仍以“银行账户”为例:
public class BankAccount {
private String accountNumber;
private BigDecimal balance;
// Getters and setters
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
}
public class BankAccountService {
public void deposit(BankAccount account, BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Deposit amount must be positive");
}
account.setBalance(account.getBalance().add(amount));
}
public void withdraw(BankAccount account, BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Withdraw amount must be positive");
}
if (account.getBalance().compareTo(amount) < 0) {
throw new IllegalStateException("Insufficient balance");
}
account.setBalance(account.getBalance().subtract(amount));
}
}
特点:BankAccount 只是一个数据载体,业务逻辑完全放在 BankAccountService 中。
三、两者对比
| 特性 | 充血模式 | 贫血模式 |
|---|---|---|
| 职责分配 | 数据和行为集中于领域对象内部 | 数据和行为分离,逻辑在服务层 |
| 代码维护性 | 高内聚,易于理解和维护 | 低内聚,服务层代码容易变得复杂 |
| 复杂度 | 初期设计复杂,需要充分理解业务 | 较简单,适合快速开发 |
| 面向对象特性 | 强调封装性、自治性,符合面向对象设计理念 | 更接近传统分层架构,弱化领域对象的作用 |
| 适用场景 | 复杂的业务逻辑和核心领域建模 | 简单场景或对性能有极高要求的系统 |
四、实践建议
- 优先选择充血模式:
- 如果系统是核心领域系统,业务逻辑复杂且变化频繁,充血模式更适合。
- 充血模式便于体现业务语义,有助于提升代码质量和可维护性。
- 选择贫血模式的场景:
- 如果系统主要以数据传递为主,例如 CRUD 型应用或数据导入导出系统。
- 在性能敏感场景(如微服务高并发)中,贫血模式较为轻量,能够减少对象行为带来的开销。
- 结合两种模式:
- 轻充血模式:在贫血模型中适当增加行为,将与数据高度相关的业务逻辑封装到对象中,其他逻辑留在服务层。
2112

被折叠的 条评论
为什么被折叠?



