首页/开发/系统分析与架构设计速查/迪米特法则(Law of Demeter)
架构风格与模式58 条

迪米特法则(Law of Demeter)

对象只与直接协作者通信,避免 a.getB().getC().doSomething() 这类链式穿越;减少对内部结构的依赖,降低改动传播范围。

什么是迪米特法则

迪米特法则(Law of Demeter,1988 年由 Lieberherr 等提出)要求对象只与直接协作者通信,不要沿着内部结构层层穿越:a.getB().getC().doSomething() 这类写法让调用方知道了 B 和 C 的内部结构。判断标准不是点号数量,而是「是否获取了对象的内部结构再据此决策」。

基本信息

通俗表述只与朋友交谈,不与陌生人交谈;不要对陌生人下指令
收益调用方不依赖被穿越对象的内部结构,改动影响面收敛
不适用链式流式 API、构建器与领域方法内部的链式调用不属于违规
落地手段由直接协作者暴露语义方法,必要时在聚合内封装计算

典型使用场景

  • 金额与折扣计算:把折扣规则封装在订单内部,避免外部穿越客户等级
  • 权限判断:由对象自身回答「能否执行」,而不是让调用方拼装条件
  • 状态校验:把多字段组合的判断放进聚合,避免散落在服务层
  • 对象组装:由工厂或仓储返回完整对象,调用方不需要自己拼路径
  • 重构存量:识别穿越点,逐个改为语义方法并补充单元测试
  • 接口设计:对外暴露语义方法而非内部字段的结构

改写示例

改写示例
# 违规:调用方了解订单、客户与等级的内部结构
price = order.get_customer().get_level().discount()
if order.get_customer().get_level().name == 'VIP': ...

# 修复:让直接协作者完成计算与判断
price = order.discount_price()          # 折扣规则封装在订单内部
if order.is_vip_customer(): ...         # 判断逻辑封装在客户/订单上

# 注意:构建器与流式 API 的链式调用不属于此类问题
request = Client.builder().timeout(3).retries(2).build()

违规与合规的界限

写法判断说明
order.customer.level.discount()违规穿越两层内部结构做决策
order.discount_price()合规语义方法,内部结构不外露
Client.builder().build()合规构建器式链式调用
list.stream().filter(..).toList()合规同对象链式流水线
判断标准是「是否获取了对象的内部结构再决策」,而不是简单的点号数量:语义方法与构建器的链式调用并不违背该原则。
为消除链式调用在中间类上堆转发方法,会带来大量样板代码,反而增加维护成本,应先考虑职责是否放错位置。

易错点

  • 为消除链式调用在中间类上加一层转发方法,样板代码激增,维护成本反升
  • 把聚合数据的拼装逻辑硬塞回对象,职责错位,对象承担了查询职责
  • 机械按点号数量判断违规,把正常的构建器与流式调用也改掉
  • 只改调用形式不改数据暴露方式,内部结构仍通过 getter 链对外敞开

数据来源与参考

  • Karl Lieberherr 等《Controlling the Complexity of Software Designs》(1988)
  • 《The Pragmatic Programmer》
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌