在面向对象系统设计中,贫血模型与充血模型是两种核心的领域建模思路,二者的差异会直接影响业务逻辑的承载方式和系统的长期可维护性。在业务变量分配这类需要频繁调整规则、关联多业务实体的场景中,选型的正确性尤为重要。

两种模型的核心定义与差异
贫血模型
贫血模型是指领域对象仅作为数据载体,只包含属性和对应的getter、setter方法,所有业务逻辑都放在服务层(Service)中实现。这种模型将数据和行为分离,领域对象本身不具备业务能力。
充血模型
充血模型是指领域对象不仅包含属性,还封装了和自身相关的所有业务逻辑,领域对象本身是行为的主体,服务层仅负责协调领域对象、处理跨领域的流程编排。
核心差异对比
| 对比维度 | 贫血模型 | 充血模型 |
|---|---|---|
| 逻辑承载位置 | 服务层 | 领域对象自身 |
| 数据与行为耦合度 | 低,二者分离 | 高,二者绑定 |
| 代码复用性 | 低,逻辑集中在服务层,难以跨场景复用 | 高,领域对象的方法可被多个场景调用 |
| 学习成本 | 低,符合传统分层架构习惯 | 高,需要理解领域驱动设计思想 |
业务变量分配场景的需求分析
业务变量分配通常指根据预设规则,将可分配的变量(如优惠额度、任务配额、资源权重等)分配给对应的业务实体(如用户、部门、商户等)。这类场景通常有以下特点:
- 分配规则可能随业务发展频繁调整,比如新增分配维度、修改权重计算逻辑
- 分配过程需要校验多个前置条件,比如用户是否满足参与资格、变量余量是否充足
- 可能涉及到多个实体之间的关联计算,比如部门配额需要汇总下属用户的分配量
两种模型的实战实现示例
贫血模型实现
首先定义数据载体类:
// 业务变量实体
public class BusinessVariable {
private String variableId;
private String variableName;
private Integer totalAmount;
private Integer usedAmount;
// getter和setter方法
public String getVariableId() {
return variableId;
}
public void setVariableId(String variableId) {
this.variableId = variableId;
}
public String getVariableName() {
return variableName;
}
public void setVariableName(String variableName) {
this.variableName = variableName;
}
public Integer getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(Integer totalAmount) {
this.totalAmount = totalAmount;
}
public Integer getUsedAmount() {
return usedAmount;
}
public void setUsedAmount(Integer usedAmount) {
this.usedAmount = usedAmount;
}
}
// 用户实体
public class User {
private String userId;
private String userName;
private Integer level;
// getter和setter方法
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
}
服务层实现分配逻辑:
public class VariableAssignService {
// 分配业务变量
public void assignVariable(BusinessVariable variable, User user, Integer assignAmount) {
// 校验变量余量
if (variable.getUsedAmount() + assignAmount > variable.getTotalAmount()) {
throw new RuntimeException("变量余量不足");
}
// 校验用户资格
if (user.getLevel() < 2) {
throw new RuntimeException("用户等级不符合要求");
}
// 执行分配
variable.setUsedAmount(variable.getUsedAmount() + assignAmount);
// 这里可以补充记录分配流水等逻辑
System.out.println("为用户" + user.getUserName() + "分配" + assignAmount + "个" + variable.getVariableName());
}
}
充血模型实现
定义封装逻辑的领域对象:
// 业务变量领域对象
public class BusinessVariable {
private String variableId;
private String variableName;
private Integer totalAmount;
private Integer usedAmount;
// 构造方法
public BusinessVariable(String variableId, String variableName, Integer totalAmount, Integer usedAmount) {
this.variableId = variableId;
this.variableName = variableName;
this.totalAmount = totalAmount;
this.usedAmount = usedAmount;
}
// 校验余量是否充足
public boolean hasEnoughAmount(Integer assignAmount) {
return this.usedAmount + assignAmount <= this.totalAmount;
}
// 执行分配,更新自身状态
public void doAssign(Integer assignAmount) {
if (!hasEnoughAmount(assignAmount)) {
throw new RuntimeException("变量余量不足");
}
this.usedAmount += assignAmount;
}
// getter方法,不提供setter,保证状态只能通过内部方法修改
public String getVariableId() {
return variableId;
}
public String getVariableName() {
return variableName;
}
public Integer getTotalAmount() {
return totalAmount;
}
public Integer getUsedAmount() {
return usedAmount;
}
}
// 用户领域对象
public class User {
private String userId;
private String userName;
private Integer level;
public User(String userId, String userName, Integer level) {
this.userId = userId;
this.userName = userName;
this.level = level;
}
// 校验是否有分配资格
public boolean canAssign() {
return this.level >= 2;
}
public String getUserId() {
return userId;
}
public String getUserName() {
return userName;
}
public Integer getLevel() {
return level;
}
}
服务层仅做协调:
public class VariableAssignService {
public void assignVariable(BusinessVariable variable, User user, Integer assignAmount) {
// 调用领域对象自身的校验逻辑
if (!user.canAssign()) {
throw new RuntimeException("用户等级不符合要求");
}
// 调用领域对象的分配逻辑
variable.doAssign(assignAmount);
System.out.println("为用户" + user.getUserName() + "分配" + assignAmount + "个" + variable.getVariableName());
}
}
业务变量分配场景的选型建议
在实际的业务变量分配场景中,选型可以参考以下维度:
- 如果业务规则简单、长期变动少,且团队对领域驱动设计不熟悉,优先选择贫血模型,开发效率高,学习成本低
- 如果业务规则复杂、需要频繁调整分配逻辑,且团队具备领域建模能力,优先选择充血模型,逻辑内聚在领域对象中,后续规则修改只需要调整对应领域对象的方法,不会影响服务层的其他逻辑,可维护性更强
- 如果是中小型项目,业务复杂度中等,可以采用折中方案:将基础校验、状态更新的逻辑放在领域对象中,复杂的跨实体编排逻辑放在服务层,兼顾开发效率和可维护性
需要注意的是,无论选择哪种模型,都要保持项目内模型风格的统一,避免部分模块用贫血模型、部分模块用充血模型,增加代码的认知成本。