迪米特法则要求每个类只与直接朋友交互,即自身、参数、成员变量、方法内new的对象;禁止链式调用如a.getB().getC(),应封装为语义明确的方法;需配合封装与不可变性,避免暴露内部结构。

在 Java 面向对象设计中运用迪米特法则,核心是控制对象间交互的“深度”和“广度”,让每个类只跟自己明确知道的对象打交道,不越级、不探路、不暴露内部结构。
明确谁是你的“直接朋友”
一个类能合法调用方法的对象,仅限于以下几类:
- 当前对象自身(this)
- 作为方法参数传入的对象
- 作为成员变量持有的对象(字段)
- 在方法体内用 new 创建的对象
- 集合中的元素不算朋友——不能写
list.get(0).getName(),而应封装查询逻辑
比如 user.getAddress().getCity().getName() 是典型违规:user 和 city 没有直接关系,中间隔了 address 和 city 两层。应改为 user.getCityName(),把导航逻辑收进 user 类内部。
消灭链式调用,封装导航逻辑
链式调用(如 a.getB().getC().doX())表面简洁,实则把多个依赖硬编码进调用方,一改全崩。
- ❌ 错误:
order.getCustomer().getProfile().getPreferredLanguage() - ✅ 正确:
order.getCustomerPreferredLanguage()(由 Order 封装语义) - ✅ 或更合理:
order.getCustomer().getPreferredLanguage()(把“偏好语言”这个业务概念归属 Customer,Order 不需知道 Profile)
关键不是删掉点号,而是让每个方法表达清晰的业务意图,而非技术路径。
用委托代替裸露访问,但避免无意义中介
当需要跨层级获取数据时,优先由“近端”对象提供封装接口,而不是让远端对象暴露内部细节。
- 把
logger.getConfig().setLevel("DEBUG")改成logger.setLevel("DEBUG"),Config 的存在对调用方透明 - 若引入一个
UserFacade类,只为转发user.getName(),那就多余;但如果它聚合了认证、权限、偏好等多维度操作,且命名体现业务价值(如UserContext.resolveCurrentLocale()),就值得存在
判断中介是否必要:它是否隐藏了组合逻辑?是否提升了语义清晰度?是否降低了调用方出错概率?
配合封装与不可变性才真正生效
迪米特法则不是孤立原则,它依赖良好的封装基础:
- 成员变量尽量 private,避免直接暴露可变集合(如返回
new ArrayList(items)或Collections.unmodifiableList(items)) - 构造时通过构造函数或 Builder 注入依赖,不给运行时随意替换内部对象留口子
- getter 方法不返回内部状态引用,而提供只读视图或计算结果(如
hasItem(String id)代替getItems())
没有封装的“朋友”,只是把紧耦合从调用链转移到了字段访问上。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











