迪米特法则(Law of Demeter, LoD),又称最少知识原则(Least Knowledge Principle),是面向对象设计的六大原则之一,由 Ian Holland 于 1987 年提出。其核心思想是:一个对象应对其他对象保持最少的了解,只与“直接朋友”交互,避免与“陌生人”耦合。这一原则旨在降低模块间的依赖关系,提升系统的可维护性和可扩展性。
一、核心思想与原则
- 什么是“直接朋友”?
迪米特法则中的“直接朋友”指以下对象:
- 当前对象自身(
this) - 方法参数传入的对象
- 当前对象的成员变量
- 当前对象创建的对象(如工厂方法返回的对象)
- 成员变量集合中的元素(如
List中的对象)
禁止行为:
// 违反LoD的链式调用(“火车残骸式代码”)
user.getOrder().getAddress().getCity();
此类调用暴露了过多内部细节,增加耦合风险。
- 核心目标
- 降低 耦合:减少类间的直接依赖,避免修改一处波及全局。
- 增强封装:隐藏实现细节,仅暴露必要接口。
- 提高模块独立性:模块功能内聚,易于复用和测试。
二、违反 vs 遵循:代码示例
案例1:汽车驾驶系统
❌ 违反 迪米特法则 Driver 直接访问 Car 的内部组件 Engine:
class Engine {
public void start() { System.out.println("Engine started"); }
}
class Car {
private Engine engine;
public Engine getEngine() { return engine; } // 暴露内部结构
}
class Driver {
public void drive(Car car) {
car.getEngine().start(); // 直接调用Engine的方法
}
}
问题:Driver 依赖 Car 和 Engine 的实现细节,若 Engine 接口变更,Driver 需同步修改。
✅ 遵循 迪米特法则 通过封装调用链,Driver 仅与 Car 交互:
class Car {
private Engine engine;
public void start() { engine.start(); } // 封装Engine的启动逻辑
}
class Driver {
public void drive(Car car) {
car.start(); // 仅调用Car的接口
}
}
优势:
Driver不再感知Engine的存在;Car的内部重构不影响Driver。
案例2:电商订单系统
❌ 违反:深层嵌套调用
// 客户端代码直接访问多层对象
String city = user.getOrder().getAddress().getCity();
问题:客户端需了解 User→Order→Address 的完整结构,耦合度高。
✅ 遵循:委托封装
class User {
private Order order;
public String getShippingCity() {
return order.getShippingCity(); // 委托给Order
}
}
class Order {
private Address address;
public String getShippingCity() {
return address.getCity(); // 委托给Address
}
}
// 客户端调用
String city = user.getShippingCity(); // 仅与User交互
优势:客户端无需知道 Order 和 Address 的存在。
三、实际应用场景
- 中介者模式 ( Mediator )
场景:UI控件交互(如按钮点击触发列表、文本框更新)。 问题:控件间直接调用导致网状耦合。 解决方案:引入中介类协调交互:
// 中介者接口
interface Mediator {
void notify(Component sender, String event);
}
// 按钮触发事件时通知中介者
button.onClick(() -> mediator.notify(button, "click"));
中介者将事件分发给相关控件,避免直接依赖。
- 服务分层架构
场景:订单服务需调用库存和支付服务。 问题:直接依赖具体服务实现。 解决方案:依赖抽象接口,通过接口隔离实现细节:
// 定义服务接口
interface InventoryService {
boolean checkStock(String productId);
}
// 订单服务通过接口调用
class OrderService {
private InventoryService inventoryService;
public void placeOrder(Order order) {
inventoryService.checkStock(order.getProductId());
}
}
四、优缺点分析
| 优点 | 缺点 |
|---|---|
| 降低耦合:模块独立性增强 | 增加中介类:可能引入过多中间层 |
| 提高可维护性:修改影响局部化 | 性能开销:间接调用增加耗时 |
| 简化测试:依赖减少,Mock更容易 | 过度设计:小型项目慎用 |
五、总结
迪米特法则的核心是 “减少知识,限制交互” :
- ✅ 只与直接朋友交谈,拒绝链式调用;
- ✅ 封装深层逻辑,通过委托隐藏细节;
- ✅ 优先依赖接口而非具体实现。