来源:mikechen的互联网架构
迪米特法则是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。
迪米特法则提出,一个软件实体,应尽可能少的与其它实体交互。当一个模块修改时,就会尽量少对其他模块产生影响,系统扩展也会更加容易。
本文主要介绍迪米特法则,力求通过图文源码,一文彻底掌握它。
建议结合前几篇看,更容易融会贯通:
微信面试:说说里氏替换原则
精通接口隔离原则
依赖倒置原则就看这篇
3 分钟吃透开闭原则
单一职责原则让代码质量提升100倍
迪米特法则(LoD),又称为最少知识原则,英文全称 Least Knowledge Principle。
迪米特法则是指一个模块(类)只和与它高度相关的模块交流,尽量减少与其他模块(类)的直接交流。
大白话就是,模块之间的通信应该简单明了,每个模块只需要关心与它高度相关的模块,而不必了解整个系统的细节。
高度相关是指这些模块可能需要共同完成一项任务,或者相互依赖,以实现某个功能。
这有点像在现实生活中,你只与你的朋友直接交流,而不会与朋友的朋友(陌生人)交流。你的任何改变,都不会对朋友的朋友带来影响。

低耦合、高内聚是软件编程的总原则。
耦合:指模块之间的联系程度。
内聚:指模块内部元素的联系程度。
模块之间了解得越多,耦合就越紧。
任何一个模块的变化,都会影响到其他模块,系统就会变得很复杂。

无论是面向过程编程,还是面向对象编程,只有尽可能降低各个模块之间的耦合,才能提高代码的复用率。
低耦合的优点不言而喻,但是怎样才能实现低耦合呢?
这正是迪米特法则要去实现的。
迪米特法则的核心思想:模块之间最少依赖。
不该有直接依赖关系的类之间,就不要有依赖;
有依赖关系的类之间,尽量只依赖必要的接口。
如果模块 A 需要与模块 B 交流,它应该直接与模块 B 通信,而不是通过模块 C、D、E ......
每个模块只与它高度相关的模块交流,减少了模块之间不必要的依赖。
使得每个模块都能更好地封装自己的功能,只暴露必要的接口,提高了模块的封装性和隔离性。
我们再来看具体示例,以便更好地理解迪米特法则及其应用。
假设:
公司要开发一款社交软件,需要研发用户发布帖子和发表评论的功能。
不遵循迪米特法则:
我们设计一个模块,让这个模块直接与用户、帖子和评论之间进行复杂的通信,以获取信息、创建帖子和评论。
// 用户模块class User {String username;public User(String username) {this.username = username;}public void createPost(String content, Post post) {// 用户创建帖子post.setContent(content);}public void addComment(String text, Post post) {// 用户添加评论Comment comment = new Comment(text);post.addComment(comment);}}// 帖子模块class Post {String content;Listcomments = new ArrayList<>(); public void setContent(String content) {this.content = content;}public void addComment(Comment comment) {comments.add(comment);}}// 评论模块class Comment {String text;public Comment(String text) {this.text = text;}}public class SocialMediaApp {public static void main(String[] args) {User user1 = new User("Alice");User user2 = new User("Bob");Post post = new Post();user1.createPost("Hello, world!", post);user2.addComment("Great post, Alice!", post);}}
虽说这样也能实现需求,但弊端很明显。
用户模块直接与帖子模块、评论模块进行通信,用户模块需要知道帖子和评论的细节,包括它们的方法和属性,甚至需要直接访问它们的数据库。
模块之间的依赖关系很紧密,导致了紧耦合。
后续任何与用户、帖子或评论相关的变化,都会影响到这个模块,系统会很复杂 。
遵循迪米特法则:
现在,我们应用迪米特法则来改进这个设计。
按照迪米特法则,一个模块应该只与它的“朋友”通信。
在这种情况下,帖子可以被认为是用户的“朋友”,评论也可以被认为是帖子的“朋友”。
用户模块只需要与帖子模块通信,以发布和管理帖子。
帖子模块只需要与评论模块通信,以允许用户发表评论。
用户模块和评论模块之间没有直接联系,它们不需要知道对方的全部细节。
class User {String username;public User(String username) {this.username = username;}public void createPost(String content, Post post) {post.setContent(content);}public void addComment(String text, Post post) {post.addComment(text);}}class Post {String content;Listcomments = new ArrayList<>(); public void setContent(String content) {this.content = content;}public void addComment(String text) {Comment comment = new Comment(text);comments.add(comment);}}class Comment {String text;public Comment(String text) {this.text = text;}}public class SocialMediaApp {public static void main(String[] args) {User user1 = new User("Alice");User user2 = new User("Bob");Post post = new Post();user1.createPost("Hello, world!", post);user2.addComment("Great post, Alice!", post);}}
用户模块只与帖子模块交互,不直接与评论模块通信,它不需要知道评论的具体细节。
评论的具体实现对用户模块是透明的,用户只需要知道与帖子相关的操作。
如果我们要对用户模块进行修改,不会影响到评论模块,因为它们没有直接联系。
这种设计减少了模块之间的依赖关系,系统更加灵活、容易扩展。
迪米特法则的核心思想是:不该有直接依赖关系的类之间,就不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。
迪米特法则避免与非直接的类通信,使模块与模块之间保持较低的耦合关系。
但是,要通信,必然就要通过“中介”来联系。过分使用迪米特原则,系统中就会存在大量“中介”,这在一定程度上增加了系统的复杂度。
应用时需要综合权衡,既要做到结构清晰,又要实现高内聚、低耦合。
建议收藏备用,架构设计筑基必知必会,大厂面试也爱问。