在面向对象编程中,代码通常会涉及两个核心问题:

  1. 数据放在哪里?
  2. 业务逻辑放在哪里?

不同项目、框架和团队,会形成不同的代码组织风格。下面总结三种常见风格:

  • 贫血模型
  • 充血模型
  • 流式接口 / 链式调用

1. 贫血模型 Anemic Domain Model

贫血模型指的是:对象本身主要只保存数据,真正的业务逻辑写在外部的 Service 层里。

也就是说,对象像一个“数据容器”,而不是一个有行为能力的对象。

典型结构

class User {
    private String name;
    private int level;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
````

然后业务逻辑写在外部:

class UserService {

public void upgrade(User user) {
    user.setLevel(user.getLevel() + 1);
}

}


### 什么是 getter / setter?

`getter` 和 `setter` 是访问对象属性的方法。

因为很多语言不推荐直接访问对象内部变量,所以会写成方法:

user.name


改成:

user.getName()
user.setName("Alice")


其中:

- `getName()`:读取 `name`
    
- `setName(...)`:修改 `name`
    

所以一个只有属性和 `getter / setter` 的类,本质上只是一个“数据盒子”。

### 常见相关术语

|术语|含义|
|---|---|
|POJO|普通 Java 对象,通常指没有复杂框架依赖的简单对象|
|Entity|实体类,通常对应数据库中的一张表|
|DTO|数据传输对象,用来在接口、服务之间传递数据|
|Service|服务层,通常写业务流程和业务逻辑|
|Transaction Script|事务脚本,把业务流程按步骤写在一个函数或服务方法里|

### 特点

贫血模型的核心是:

> 数据和逻辑分离。

对象只负责保存数据,Service 负责处理逻辑。

### 优点

- 简单直观
    
- 适合 CRUD 项目
    
- 和数据库表结构容易对应
    
- 在 Spring、MyBatis 等后端框架中很常见
    

### 缺点

- 对象缺少行为,只是数据容器
    
- 复杂业务会导致 Service 层越来越臃肿
    
- 代码容易变成“面向对象语法包装下的面向过程代码”
    

---

## 2. 充血模型 Rich Domain Model

充血模型指的是:对象不仅保存数据,还包含与自身相关的业务行为。

也就是说,对象不只是“数据盒子”,而是一个真正能处理自己业务逻辑的领域对象。

### 典型结构

class User {

private int level;

public void upgrade() {
    this.level += 1;
}

}


调用时:

user.upgrade();


这里的 `upgrade` 逻辑写在 `User` 自己内部,而不是外部的 `UserService` 里。

### 核心思想

充血模型的核心是:

> 数据和逻辑合一。

谁拥有数据,谁就更适合处理相关逻辑。

### 相关设计原则

|原则|含义|
|---|---|
|Encapsulation 封装|对象隐藏内部细节,只暴露必要行为|
|Tell, Don't Ask|不要把对象的数据拿出来自己判断,而是直接让对象执行行为|
|Information Expert|逻辑应该放在最了解相关数据的对象中|
|Domain Entity|领域实体,既有状态,也有行为|

### Tell, Don't Ask 示例

不推荐:

if (user.getLevel() < 10) {

user.setLevel(user.getLevel() + 1);

}


更推荐:

user.upgrade();


因为升级规则属于 `User` 自己,外部代码不应该到处读取它的属性再自己判断。

### 优点

- 更符合面向对象思想
    
- 业务规则集中在领域对象中
    
- 复杂业务更容易维护
    
- 能减少 Service 层过度膨胀
    

### 缺点

- 设计难度更高
    
- 对简单 CRUD 来说可能显得过度设计
    
- 和数据库表、ORM 框架配合时需要更多设计经验
    

---

## 3. 流式接口 Fluent Interface / 链式调用 Method Chaining

流式接口是一种让代码读起来更连贯的调用风格。

它的典型形式是连续调用方法:

user.setName("Alice")

.setAge(18)
.setEmail("alice@example.com");

或者:

QueryBuilder

.select("name")
.from("user")
.where("age > 18")
.orderBy("id");

### 核心原理

链式调用通常依赖一个简单机制:

> 每个方法执行完之后,返回对象本身 `this`。

例如:

class UserBuilder {

private String name;
private int age;

public UserBuilder name(String name) {
    this.name = name;
    return this;
}

public UserBuilder age(int age) {
    this.age = age;
    return this;
}

}


这样就可以写成:

new UserBuilder()

.name("Alice")
.age(18);

### 相关术语

|术语|含义|
|---|---|
|Method Chaining|链式调用,连续调用多个方法|
|Fluent Interface|流式接口,让代码像自然语言一样流畅|
|Builder Pattern|构建者模式,用链式方法一步步构造复杂对象|
|DSL|领域特定语言,让代码更接近某个业务领域的表达方式|

### 适用场景

流式接口常见于:

- 配置类
    
- 构建对象
    
- SQL 查询构造器
    
- 测试代码
    
- UI 构造
    
- API 调用参数组装
    

### 优点

- 代码简洁
    
- 可读性强
    
- 适合表达连续配置或构造过程
    
- 适合做内部 DSL
    

### 缺点

- 如果链条过长,可读性反而下降
    
- 调试时不一定方便
    
- 不适合所有业务逻辑
    
- 可能隐藏中间状态变化
    

---

## 三者对比

|风格|核心思想|数据和逻辑关系|典型代码|
|---|---|---|---|
|贫血模型|对象保存数据,Service 处理逻辑|数据与逻辑分离|`userService.upgrade(user)`|
|充血模型|对象自己处理与自身相关的行为|数据与逻辑合一|`user.upgrade()`|
|流式接口|通过连续方法调用提高表达力|关注调用形式,不直接决定业务模型|`.name().age().build()`|

---

## 需要注意的区别

贫血模型和充血模型讨论的是:

> 业务逻辑应该放在哪里。

而流式接口讨论的是:

> 方法调用应该如何组织得更流畅。

所以它们不是同一维度的概念。

一个项目可以同时使用这些风格:

- 简单 CRUD:使用贫血模型
    
- 复杂业务规则:使用充血模型
    
- 配置、构造、查询:使用流式接口
    

---

## 简单理解

可以这样记:

|类型|类比|说明|
|---|---|---|
|贫血模型|表格 + 管理员|对象只是数据,Service 负责处理|
|充血模型|有职责的人|对象既有数据,也知道自己该怎么做|
|流式接口|连续填表 / 点餐|方法一个接一个调用,表达更顺滑|

---

## 我的理解

贫血模型并不是完全错误。它在很多后端项目中非常常见,尤其适合简单业务、数据库驱动的 CRUD 系统。

充血模型更适合复杂业务,因为它能把业务规则封装进对象内部,减少外部 Service 对对象内部状态的随意操作。

流式接口本质上是一种 API 设计风格,它不直接决定业务逻辑放在哪里,而是让调用代码更自然、更容易组织。

因此,判断一种代码风格好不好,不能只看它是不是“面向对象”,还要看业务复杂度、团队习惯、框架限制和维护成本。


核心可以记成一句话:

**贫血 / 充血是在讨论“逻辑放哪”;流式接口是在讨论“调用怎么写得顺”。**

---