lod的核心是“少暴露依赖”而非“少写代码”,即类只与直接朋友(this、参数、方法内new对象)通信,避免链式调用如order.getcustomer().getaddress().getcity(),应通过委托方法封装并处理空值。

LoD 不是“少写代码”,而是“少暴露依赖”
迪米特法则(LoD)常被误解为“类里调用的方法越少越好”,其实核心是:一个类应该只和**直接朋友**通信,不通过中间对象链式访问下游属性或方法。比如 order.getCustomer().getAddress().getCity() 就违反了 LoD —— order 和 city 不是朋友,中间穿过了 getCustomer() 和 getAddress() 两层。
这么做的原因很实际:一旦 Customer 类重构,把 getAddress() 改成 fetchContactInfo(),或者 Address 类移除了 getCity(),所有链式调用的地方全挂——错误信息可能是 NullPointerException 或 NoSuchMethodError,但根因藏得深,排查成本高。
- 真正的朋友只有三类:
this、方法参数、方法内 new 出来的对象(不含构造器注入的依赖) - Spring 管理的 Bean(如
@Autowired注入的 service)不算“直接朋友”,不能在业务逻辑里链式调它返回值的子方法 - DTO/VO 层可以适度放宽(毕竟只是数据载体),但领域模型、Service、Repository 层必须严格守界
怎么改?用“委托方法”代替“穿透访问”
面对 order.getCustomer().getAddress().getCity() 这种写法,别急着加工具类或静态方法,优先在 Order 类里加一个委托方法:
public String getCustomerCity() {
return customer != null && customer.getAddress() != null
? customer.getAddress().getCity()
: null;
}
这样调用方只需写 order.getCustomerCity(),既清晰又可控。如果后续地址逻辑变复杂(比如要支持国际格式、缓存城市名),改的只是 Order 自己的方法,不影响上游。
立即学习“Java免费学习笔记(深入)”;
- 委托方法里要处理空指针,别把
NullPointerException推给调用方 - 不要为了“符合 LoD”而在
Order里复制Address的全部行为,只暴露必要语义(如getCustomerCity()),不是getCustomerAddressStreet()+getCustomerAddressZip()+ … - 如果多个类都要取客户城市,说明这个逻辑可能该上提到
CustomerService,由它统一提供findCityByOrderId(Long id)—— 此时Order反而不该有getCustomerCity()
Builder 模式和 Fluent API 是 LoD 的常见陷阱
用 new UserBuilder().name("a").email("b").address(new AddressBuilder().city("c").build()).build() 看似链式优雅,但实际让 UserBuilder 强依赖了 AddressBuilder 的内部结构。一旦 AddressBuilder 增加校验逻辑、改构造方式,所有用到它的 builder 都得跟着动。
- Builder 类之间应解耦:让
UserBuilder.address(Address address)接收成品对象,而不是暴露其构建过程 - Fluent API 的每个
.xxx()方法应返回this,且不引入新类型依赖;返回AddressBuilder就打破了封装边界 - Lombok 的
@Builder默认安全,因为它不生成跨类型的链式跳转;但自定义 builder 时极易踩坑
IDE 能帮你看出来,但不会替你设计
IntelliJ 可以用 Inspection 检出“chained method calls”,Eclipse 也有类似规则,但它们只能标出 a.getB().getC().doX() 这种明显链式,识别不了语义等价的“隐式穿透”,比如:
public class OrderService {
public void process(Order order) {
Customer c = customerRepo.findById(order.getCustomerId());
String city = c.getAddress().getCity(); // 这里又穿透了
}
}
这种写法 IDE 很难告警,但它一样违反 LoD —— OrderService 本不该知道 Customer 内部怎么存地址。
- 真正的难点不在语法层面,而在职责划分:谁该负责解释“订单的客户城市”这个业务概念?是
Order自己?CustomerService?还是单独的LocationQueryService? - 团队里最容易忽略的是测试友好性:违反 LoD 的代码,mock 起来特别费劲,往往要 mock 三层对象才能跑通一个单元测试
- LoD 不是银弹,过度遵守会导致大量薄薄的委托方法,反而模糊主干逻辑——关键看那个“穿透点”是否稳定、是否属于当前类的合理责任范围










