设计模式学习之什么是迪米特法则?

114 阅读3分钟

迪米特法则(Law of Demeter, LoD),又称最少知识原则(Least Knowledge Principle),是面向对象设计的六大原则之一,由 Ian Holland 于 1987 年提出。其核心思想是:一个对象应对其他对象保持最少的了解,只与“直接朋友”交互,避免与“陌生人”耦合。这一原则旨在降低模块间的依赖关系,提升系统的可维护性和可扩展性。


一、核心思想与原则

  1. 什么是“直接朋友”?

迪米特法则中的“直接朋友”指以下对象:

  • 当前对象自身this
  • 方法参数传入的对象
  • 当前对象的成员变量
  • 当前对象创建的对象(如工厂方法返回的对象)
  • 成员变量集合中的元素(如 List 中的对象)

禁止行为

// 违反LoD的链式调用(“火车残骸式代码”)  
user.getOrder().getAddress().getCity();  

此类调用暴露了过多内部细节,增加耦合风险。

  1. 核心目标
  • 降低 耦合:减少类间的直接依赖,避免修改一处波及全局。
  • 增强封装:隐藏实现细节,仅暴露必要接口。
  • 提高模块独立性:模块功能内聚,易于复用和测试。

二、违反 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 依赖 CarEngine 的实现细节,若 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();  

问题:客户端需了解 UserOrderAddress 的完整结构,耦合度高。

✅ 遵循:委托封装

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交互  

优势:客户端无需知道 OrderAddress 的存在。


三、实际应用场景

  1. 中介者模式 Mediator

场景:UI控件交互(如按钮点击触发列表、文本框更新)。 问题:控件间直接调用导致网状耦合。 解决方案:引入中介类协调交互:

// 中介者接口  
interface Mediator {  
    void notify(Component sender, String event);  
}  

// 按钮触发事件时通知中介者  
button.onClick(() -> mediator.notify(button, "click"));  

中介者将事件分发给相关控件,避免直接依赖。

  1. 服务分层架构

场景:订单服务需调用库存和支付服务。 问题:直接依赖具体服务实现。 解决方案依赖抽象接口,通过接口隔离实现细节:

// 定义服务接口  
interface InventoryService {  
    boolean checkStock(String productId);  
}  

// 订单服务通过接口调用  
class OrderService {  
    private InventoryService inventoryService;  
    public void placeOrder(Order order) {  
        inventoryService.checkStock(order.getProductId());  
    }  
}  

四、优缺点分析

​​优点​​​​缺点​​
​​降低耦合​​:模块独立性增强​​增加中介类​​:可能引入过多中间层
​​提高可维护性​​:修改影响局部化​​性能开销​​:间接调用增加耗时
​​简化测试​​:依赖减少,Mock更容易​​过度设计​​:小型项目慎用

五、总结

迪米特法则的核心是 “减少知识,限制交互”

  • 只与直接朋友交谈,拒绝链式调用;
  • 封装深层逻辑,通过委托隐藏细节;
  • 优先依赖接口而非具体实现。