迪米特法则(LoD)要求类只与“朋友”变量交互,即成员变量、方法参数、返回值类型;局部new、链式导航获取的对象属“陌生人”,需通过封装方法或依赖注入解耦,避免隐式耦合。
迪米特法则(lod)不是为了限制调用,而是为了让类更“守本分”——只管好自己该管的对象,不伸手去碰别人家的内部结构。它真正解决的问题,是变量层面的隐式耦合:比如一个方法里临时 new 一个对象、从返回值里连续取属性、或把非参数/非成员的对象当“顺手工具”用。这些操作看似无害,实则悄悄把多个类绑在了一起。
哪些变量算“朋友”,哪些是“陌生人”
判断变量是否合规,关键看它出现在代码的哪个位置:
- 成员变量声明中的类型:是朋友(如 private OrderService orderService;)
- 方法参数列表里的类型:是朋友(如 public void handle(Order order, User user))
- 方法返回值类型:是朋友(如 public Cart getCart(),Cart 就算朋友)
- 局部变量中 new 出来的、或从非朋友对象链上获取的:属于陌生人(如 order.getCustomer().getAddress().getCity() 中的 Address 和 City)
避免“链式导航”带来的变量污染
链式调用(a.getB().getC().doX())表面简洁,实际让 a 间接依赖了 b 的内部结构和 c 的存在形式。一旦 B 类重构 getC() 返回类型,或 C 类改名,所有调用处都会报错。
更稳妥的做法是:把跨层访问逻辑收口到被调用方内部。
- 把 order.getCustomer().getEmail() 改成 order.getCustomerEmail()
- 把 config.getDatabase().getUrl() 封装为 config.getDatabaseUrl()
- 必要时在中间类加一层语义化方法,而不是靠外部拼接路径
用参数传递代替内部构造
常见反模式:方法体内自行 new 对象,或从静态工具类拿实例。
- ❌ new PaymentValidator().validate(payment)
- ❌ StringUtils.isEmpty(name)(若 StringUtils 是无状态工具类尚可;但若它内部持有了配置或上下文,就构成隐式依赖)
- ✅ 改为接收 PaymentValidator validator 作为参数,由调用方决定用哪个实现
- ✅ 或通过构造函数/Setter 注入,确保依赖关系显式、可控
集合元素的特殊处理
集合本身是朋友,但它的泛型元素是否算朋友?答案是:是——但仅限于你直接遍历或操作它时。问题常出在“取出来再转一手”。
- ✅ for (OrderItem item : order.getItems()) { item.process(); } —— 合规,item 是集合元素,属于朋友
- ❌ OrderItem item = order.getItems().get(0); item.getPrice(); —— 不推荐,绕过集合直接索引,暴露了内部存储细节
- ✅ 更好写法:order.getFirstItem().ifPresent(i -> i.getPrice());,把“取第一个”的逻辑封装进 Order 类
不复杂但容易忽略:变量的来源比用法更重要。盯住 new、getXXX() 链、静态调用这三处,就能守住 LoD 的第一道防线。










