中间人坏味道是什么
类的大部分方法只是把调用转发给另一个对象:方法体一行,参数原样传递,返回值直接返回。这类转发层层叠加后,读代码要跨越多个同名不同层的方法,改动也要跟着穿透。它与过长的消息链是同一问题的两个极端——链太长要封装,封装过头又成了中间人。
基本信息
| 信号 | 超过一半方法是一行转发 |
|---|---|
| 手法 | Remove Middle Man、Inline Class |
| 对照 | 与 Hide Delegate 是相反方向的拉锯 |
| 判断标准 | 转发方法是否携带额外语义 |
判断与处理
- 转发方法没有任何附加逻辑:校验、组合、转换都不做,只是原样转交
- 类名与职责说不清:既不是领域概念,也不做协调
- 调用方总是越过它直接拿到内层对象:说明它没有提供额外价值
- 转发带来的价值是隔离变化来源:接口为多实现准备,即使当前只有一个实现也值得保留
- 转发方法承担了校验或事务语义:不要删,反而应当把这类职责集中
- 只是为了让类看起来完整而补齐的一批同名方法:这类最该内联
内联方向
内联方向
// 之前:Account 的每个方法都在转给 ledger
class Account {
double balance() { return this.ledger.balance(); }
double total() { return this.ledger.total(); }
Date openedAt() { return this.ledger.openedAt(); }
}
// 之后:依赖方向清晰时直接持有 ledger
class Report {
private final Ledger ledger; // 由容器注入
double balance() { return ledger.balance(); }
}
// 保留转发的情况:转发里带了计算或校验
public double available() {
return ledger.balance() - ledger.holds(); // 有计算,属于服务而非噪音
}保留还是内联
| 情况 | 处理 | 理由 |
|---|---|---|
| 纯转发、无校验无组合 | 内联 | 只增加跳转,不提供价值 |
| 转发含校验或事务语义 | 保留 | 职责集中在此处 |
| 接口为多实现准备 | 保留 | 隔离变化,替换实现不动调用方 |
| 调用方总要绕过它 | 内联 | 说明封装没有形成有效边界 |
为减转发直接暴露内部依赖会破坏原有边界:如果那个依赖是基础设施细节,调用方从此与存储或协议绑在一起,后续替换成本反而更高。内联前先确认依赖方向本身是稳定的。
把有校验职责的转发当噪音删掉,校验逻辑会随之丢失——这是内联中最容易犯的错误。删除前把每个转发方法的实现读一遍,别只看方法签名。