设计模式-迪米特法则(Law of Demeter, LoD)


迪米特法则(Law of Demeter, LoD)

别名 :最少知识原则(Least Knowledge Principle)
核心思想:一个对象应尽可能少地与其他对象发生交互,只与直接的朋友(成员变量、方法参数、方法返回值中的对象)通信,避免依赖间接的类。


原理详解

  1. 直接朋友的定义:

    • 当前对象的成员变量。
    • 当前对象方法的参数。
    • 当前对象方法的返回值。
    • 当前对象方法中创建的对象(不推荐,但允许)。
  2. 禁止链式调用 :

    避免出现 a.getB().getC().doSomething() 的调用形式,这种"火车残骸式"代码会增加耦合性。

  3. 目标:

    • 降低耦合:减少模块间的依赖,提升代码可维护性。
    • 提高封装性:隐藏内部实现细节,仅暴露必要接口。

应用案例

场景1:订单系统获取用户配送地址
错误设计(违反迪米特法则)
java 复制代码
public class User {
    private Order order;
    public Order getOrder() { return order; }
}

public class Order {
    private Address shippingAddress;
    public Address getShippingAddress() { return shippingAddress; }
}

public class Address {
    private String city;
    public String getCity() { return city; }
}

// 客户端代码:链式调用(直接访问深层对象)
User user = new User();
String city = user.getOrder().getShippingAddress().getCity();

问题:

  • 客户端需要了解 User、Order、Address 的内部结构。
  • 修改 Order 或 Address 的结构会影响客户端代码。
正确设计(遵循迪米特法则)
java 复制代码
public class User {
    private Order order;
    public String getShippingCity() {
        return order.getShippingCity(); // 委托给 Order 类
    }
}

public class Order {
    private Address shippingAddress;
    public String getShippingCity() {
        return shippingAddress.getCity(); // 委托给 Address 类
    }
}

public class Address {
    private String city;
    public String getCity() { return city; }
}

// 客户端代码:仅与直接朋友交互
User user = new User();
String city = user.getShippingCity();

优势:

  • 客户端只需与 User 交互,无需了解 Order 和 Address 的细节。
  • 修改 Order 或 Address 的结构不会影响客户端代码。

场景2:文件系统目录结构遍历
错误设计(违反迪米特法则)
java 复制代码
public class Directory {
    private List<File> files;
    private List<Directory> subDirectories;
    public List<File> getFiles() { return files; }
    public List<Directory> getSubDirectories() { return subDirectories; }
}

// 客户端代码:直接遍历深层结构
public void printAllFiles(Directory root) {
    for (File file : root.getFiles()) {
        System.out.println(file.getName());
    }
    for (Directory subDir : root.getSubDirectories()) {
        printAllFiles(subDir); // 递归调用暴露内部结构
    }
}

问题:

  • 客户端需要了解目录的递归结构,耦合度高。
正确设计(遵循迪米特法则)
java 复制代码
public class Directory {
    private List<File> files;
    private List<Directory> subDirectories;
    
    // 封装遍历逻辑,客户端无需了解内部结构
    public void traverseFiles(Consumer<File> fileConsumer) {
        files.forEach(fileConsumer);
        subDirectories.forEach(subDir -> subDir.traverseFiles(fileConsumer));
    }
}

// 客户端代码:仅调用高层方法
Directory root = new Directory();
root.traverseFiles(file -> System.out.println(file.getName()));

优势:

  • 客户端仅依赖 Directory 的 traverseFiles 方法,不关心内部实现。
  • 目录结构的遍历逻辑被封装,修改不影响客户端。

迪米特法则的实践意义

  1. 减少耦合:模块间通过接口通信,而非直接操作内部对象。
  2. 提升可维护性:修改一个类的内部结构时,无需调整其他模块。
  3. 增强可测试性:依赖越少,单元测试越容易隔离和模拟。

常见违反场景及修复

违反场景 修复方法
链式调用(a.getB().getC()) 封装中间调用,提供高层接口(如 a.getC())
暴露集合内部结构 返回不可变集合或迭代器,避免直接操作
方法参数传递复杂对象 拆分参数为基本类型或接口

总结

迪米特法则通过限制对象间的交互范围,推动代码向高内聚、低耦合 的方向演进。其核心是封装 和委托,适用于任何需要降低依赖关系的场景,尤其在大型系统或模块化架构中价值显著。

相关推荐
拆房老料3 小时前
DeepSeek Super-agent 架构解析:从单任务 Agent 到持续运行的任务系统
人工智能·设计模式·系统架构
工一木子8 小时前
设计模式与生活⑩:地铁没变,你换了个状态
设计模式
cakeism8259 小时前
2026 中国企业 DevOps 平台选型:从工具链到一体化平台的决策框架
运维·servlet·devops
是2的10次方啊10 小时前
设计模式与生活⑪:地铁里被打扰,别总靠自己硬扛
设计模式
高晶1 天前
SPMI 控制器 RTL 设计与验证新手入门
设计模式·程序员·架构
geovindu1 天前
rust:N-Barrier Pattern
开发语言·后端·设计模式·rust·屏障模式·并发型模式
顧棟1 天前
OpenAPI 网关:鉴权 + 流量治理 一体化设计方案与实现
java·spring boot·设计模式
拆房老料1 天前
【第四部分:Agentic 智能体设计模式】17. Agent 路由模式详解:从意图识别到动态模型选择
java·设计模式·ai编程·策略模式
是2的10次方啊1 天前
设计模式与生活⑩:地铁没变,你换了个状态
设计模式
瑞码空间2 天前
JSP与Servlet核心开发与CRUD实战笔记
java·笔记·servlet·jsp