迪米特法则禁止通过中间对象暴露内部细节间接访问第三方,如 user.getAddress().getCity() 将 User 与 Address 结构紧耦合;应由 User 提供 getCity() 委托调用,或交由服务层组合数据。

为什么调用 user.getAddress().getCity() 违反迪米特法则
这不是语法错误,但暴露了类之间过度耦合。迪米特法则不禁止链式调用,而是反对“通过中间对象暴露其内部细节来间接访问第三方”。user.getAddress() 返回一个 Address 实例,而你立刻拿它去调用 getCity(),等于把 User 和 Address 的内部结构绑死——一旦 Address 类重构(比如城市字段拆成 Location 嵌套),所有类似代码全得改。
常见错误现象:NullPointerException 频发、单元测试难写(要 mock 两层)、修改 Address 时不敢动 getCity() 因为不知谁在链式调用它。
- 正确做法:让
User自己提供getCity()方法,内部委托给address.getCity() - 不要让调用方感知
User是怎么存地址的——哪怕它其实是String拼接,只要接口一致,就不该影响上层 - 如果
User确实不该知道“城市”,那说明职责错位,该由业务服务层(如UserProfileService)来组合数据
public void process(User user) 里该不该直接读 user.getOrders().size()
不该。这等于让 process 方法依赖 User 内部持有 List<order></order> 的实现细节。更糟的是,如果 getOrders() 返回的是未初始化的懒加载代理(比如 Hibernate 场景),这里会触发 N+1 查询或 LazyInitializationException。
使用场景:批量处理用户订单状态、生成报表、风控校验等需要订单数量的逻辑。
立即学习“Java免费学习笔记(深入)”;
- 推荐方案:在
User类中加一个getOrderCount()方法,内部做空判断或委托给 DAO - 如果订单集合本身是必需的(比如要遍历处理),那就明确接收
List<order></order>作为参数,而不是从User上硬挖 - 性能影响:直接调
getOrders().size()可能触发全量加载;而getOrderCount()可以走COUNT(*)SQL,快一个数量级
哪些类算“朋友类”?private、package-private、protected 怎么选
迪米特法则里的“朋友”只指:当前类以字段形式持有的对象、方法参数传入的对象、方法内 new 出来的对象。不是靠访问修饰符决定的,而是看**引用来源是否受控**。
容易踩的坑:以为把字段设成 private 就安全了,结果方法里 return 了这个字段的引用,外部照样能链式调用——这叫“隐式泄露”。真正关键的是返回值类型是否可变、是否封装了内部结构。
-
private List<order> orders;</order>→ 危险!应改为private final List<order> orders = new ArrayList();</order>并且不提供 getter,或只返回Collections.unmodifiableList(orders) -
package-private适合模块内协作类(如UserRepository和UserMapper),但别让它成为跨包调用的后门 -
protected要慎用:子类继承后可能意外暴露父类内部结构,除非明确设计为模板方法模式的一部分
Spring 中 @Service 类调用 @Repository 方法算违反迪米特吗
不算。因为 @Service 显式依赖 @Repository(通过构造器注入),且调用的是 userRepository.findByUserId(id) 这类高阶语义方法,没碰 UserRepository 内部怎么连数据库、用什么 JdbcTemplate 实例——这些才是它不该知道的。
真正的违规是:在 UserService 里拿到 UserEntity 后,又去调它的 getJdbcTemplate().update(...),或者把 DataSource 从 Repository 里挖出来自己用。
- 分层边界必须清晰:Service 不该知道 Entity 的字段映射关系,更不该操作 JDBC 底层
- 如果 Service 需要复杂查询,应该扩展 Repository 接口(加新方法),而不是绕过它
- 兼容性影响:违反这点会导致换 ORM(比如从 MyBatis 换到 JPA)时,Service 层要大面积重写










