07.迪米特原则介绍

news/2025/3/19 19:13:13/文章来源:https://www.cnblogs.com/yc211/p/18781614

07.迪米特原则介绍

目录介绍

  • 01.问题思考的分析
  • 02.学习迪米特原则目标
  • 03.理解迪米特原则
  • 04.迪米特原则思想
  • 05.迪米特原则案例1
  • 06.迪米特原则案例2
  • 07.迪米特原则思考
  • 08.迪米特原则的总结

01.问题思考的分析

  1. 什么是迪米特原则,这个原则如何理解,如何运用到实际开发,举例说明一下?
  2. 什么是高内聚松耦合,能否举例说明一下?

迪米特法则。尽管它不像 SOLID、KISS、DRY 原则那样,人尽皆知,但它却非常实用。利用这个原则,能够帮我们实现代码的“高内聚、松耦合”。

今天,就围绕下面几个问题,并结合两个代码实战案例,来深入地学习这个法则。

  • 什么是“高内聚、松耦合”?
  • 如何利用迪米特法则来实现“高内聚、松耦合”?
  • 有哪些代码设计是明显违背迪米特法则的?对此又该如何重构?

02.学习迪米特原则目标

迪米特原则(Law of Demeter)也被称为最少知识原则(Principle of Least Knowledge),是面向对象设计中的一个重要原则。

能够清晰知道该原则的思想,并且可以应用到实际代码中,学习的目标是降低类之间的耦合性,提高代码的可维护性和可扩展性。

03.理解迪米特原则

3.1 迪米特原则介绍

迪米特法则的英文翻译是:Law of Demeter,缩写是 LOD。不过,它还有另外一个更加达意的名字,叫作最小知识原则,英文翻译为:The Least Knowledge Principle。

关于这个设计原则,我们先来看一下它最原汁原味的英文定义:

Each unit should have only limited knowledge about other units: only units “closely” related to the current unit. Or: Each unit should only talk to its friends; Don’t talk to strangers.

把它直译成中文: 每个模块(unit)只应该了解那些与它关系密切的模块(units: only units “closely” related to the current unit)的有限知识(knowledge)。或者说,每个模块只和自己的朋友“说话”(talk),不和陌生人“说话”(talk)。

3.2 迪米特原则由来

类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。这个时候要降低类之间耦合!

3.3 内聚和耦合关系

“高内聚”有助于“松耦合”,同理,“低内聚”也会导致“紧耦合”。

类的粒度比较小,每个类的职责都比较单一。相近的功能都放到了一个类中,不相近的功能被分割到了多个类中。这样类更加独立,代码的内聚性更好。因为职责单一,所以每个类被依赖的类就会比较少,代码低耦合。一个类的修改,只会影响到一个依赖类的代码改动。我们只需要测试这一个依赖类是否还能正常工作就行了。

类粒度比较大,低内聚,功能大而全,不相近的功能放到了一个类中。这就导致很多其他类都依赖这个类。当我们修改这个类的某一个功能代码的时候,会影响依赖它的多个类。这也就是所谓的“牵一发而动全身”。

3.3 何为高内聚松耦合

“高内聚、松耦合”是一个非常重要的设计思想。可以看案例视频播放器View如何和Player解耦合

能够有效地提高代码的可读性和可维护性,缩小功能改动导致的代码改动范围。很多设计原则都以实现代码的“高内聚、松耦合”为目的,比如单一职责原则、基于接口而非实现编程等。

在这个设计思想中,“高内聚”用来指导类本身的设计,“松耦合”用来指导类与类之间依赖关系的设计。

所谓高内聚,就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一个类中。相近的功能往往会被同时修改,放到同一个类中,修改会比较集中,代码容易维护。

所谓松耦合是说,在代码中,类与类之间的依赖关系简单清晰。即使两个类有依赖关系,一个类的代码改动不会或者很少导致依赖类的代码改动。

04.迪米特原则思想

迪米特原则的核心观念 : 就是类之间的解耦,解耦是有一定程度的,尽量做到弱耦合,耦合程度越低,类的复用率才能提高。由于减少了类之间不必要的依赖,从而达到了降低了耦合的目的。

迪米特法则的原理是通过封装和信息隐藏来实现对象之间的松耦合。每个对象只需要知道与之直接交互的对象的接口,而不需要了解对象的内部实现细节。

05.迪米特原则案例1

例子:有一个集团公司,下属单位有分公司和直属部门,现在要求打印出所有下属单位的员工ID。先来看一下违反迪米特法则的设计。

// 总公司员工
class Employee {private String id;public String getId() {return id;}public void setId(String id) {this.id = id;}
}// 分公司员工
class SubEmployee {private String id;public String getId() {return id;}public void setId(String id) {this.id = id;}
}// 子公司管理
class SubCompanyManager {public List<SubEmployee> getAllEmployee() {List<SubEmployee> list = new ArrayList<>();for (int i = 1; i < 5; i++) {SubEmployee subEmployee = new SubEmployee();// 给分公司人员顺序分配一个IDsubEmployee.setId("分公司" + i);list.add(subEmployee);}return list;}
}// 总公司管理
class CompanyManager {public List<Employee> getAllEmployee() {List<Employee> list = new ArrayList<>();for (int i = 1; i < 3; i++) {Employee employee = new Employee();// 给总公司人员顺序分配一个IDemployee.setId("总公司" + i);list.add(employee);}return list;}public void printAllEmployee(SubCompanyManager subCompanyManager) {// 分公司员工List<SubEmployee> subEmployeeList = subCompanyManager.getAllEmployee();for (SubEmployee subEmployee : subEmployeeList) {System.out.println(subEmployee.getId());}// 总公司员工List<Employee> employeeList = getAllEmployee();for (Employee employee : employeeList) {System.out.println(employee.getId());}}
}

问题出现在CompanyManager类,根据迪米特法则,只与直接的朋友发生通信。

而SubEmployee类并不是CompanyManager类的直接朋友(以局部变量出现的耦合不属于直接朋友),从逻辑上讲总公司只与他的分公司耦合就行了,与分公司的员工并没有任何联系,这样设计显然是增加了不必要的耦合。

按照迪米特法则,应该避免类中出现这样非直接朋友关系的耦合。修改后的代码如下

// 总公司员工
class Employee {private String id;public String getId() {return id;}public void setId(String id) {this.id = id;}
}// 分公司员工
class SubEmployee {private String id;public String getId() {return id;}public void setId(String id) {this.id = id;}
}// 子公司管理
class SubCompanyManager {public List<SubEmployee> getAllEmployee() {List<SubEmployee> list = new ArrayList<>();for (int i = 1; i < 5; i++) {SubEmployee subEmployee = new SubEmployee();// 给分公司人员顺序分配一个IDsubEmployee.setId("分公司" + i);list.add(subEmployee);}return list;}public void printSubCompany() {List<SubEmployee> subEmployeeList = this.getAllEmployee();for (SubEmployee subEmployee : subEmployeeList) {System.out.println(subEmployee.getId());}}
}// 总公司管理
class CompanyManager {public List<Employee> getAllEmployee() {List<Employee> list = new ArrayList<>();for (int i = 1; i < 3; i++) {Employee employee = new Employee();// 给总公司人员顺序分配一个IDemployee.setId("总公司" + i);list.add(employee);}return list;}public void printAllEmployee(SubCompanyManager subCompanyManager) {// 分公司员工subCompanyManager.printSubCompany();// 总公司员工List<Employee> employeeList = getAllEmployee();for (Employee employee : employeeList) {System.out.println(employee.getId());}}
}public class Client {public static void main(String[] args) {CompanyManager companyManager = new CompanyManager();companyManager.printAllEmployee(new SubCompanyManager());}
}

修改后,为分公司增加了打印人员ID的方法,总公司直接调用来打印,从而避免了与分公司的员工发生耦合。

06.迪米特原则案例2

上体育课,我们经常有这样一个场景:体育老师上课前要体育委员确认一下全班女生到了多少位,也就是体育委员清点女生的人数。

public class Teacher{//老师对体育委员发一个命令,让其清点女生人数public void command(GroupLeader groupLeader){List<Girl> listGirls = new ArrayList();//初始化女生for(int i=0;i<20;i++){listGirls.add(new Girl());}//告诉体育委员开始清点女生人数groupLeader.countGirls(listGirls);}
}public class GroupLeader{//清点女生数量public void countGirls(List<Girl> listGirls){System.out.println("女生人数是:"+listGirls.size());}
}publci class Girl{
}public class Client{public static void main(Strings[] args){Teacher teacher = new Teacher();//老师给体育委员发清点女生人数的命令teacher.command(new GroupLeader());}
}

我们再回头看Teacher类,Teacher类只有一个朋友类GroupLeader,Girl类不是朋友类,但是Teacher与Girl类通信了,这就破坏了Teacher类的健壮性,Teacher类的方法竟然与一个不是自己的朋友类Girl类通信,这是不允许的,严重违反了迪米特原则。

public class Teacher{//老师对体育委员发一个命令,让其清点女生人数public void command(GroupLeader groupLeader){//告诉体育委员开始清点女生人数groupLeader.countGirls();}
}public class GroupLeader{private List<Girl> listGirls;public GroupLeader(List<Girl> listGirls){this.listGirls = listGirls;}//清点女生数量public void countGirls(){System.out.println("女生人数是:"+listGirls.size());}
}public class Client{public static void main(Strings[] args){//产生女生群体List<Girl> listGirls = new ArrayList<Girl>();//初始化女生for(int i=0;i<20;i++){listGirls.add(new Girl());}Teacher teacher = new Teacher();//老师给体育委员发清点女生人数的命令teacher.command(new GroupLeader(listGirls));}
}

对程序修改,把Teacher中对Girl群体的初始化移动到场景类中,同时在GroupLeader中增加对Girl的注入,避开了Teacher类对陌生类Girl的访问,降低了系统间的耦合,提高了系统的健壮性。

07.迪米特原则思考

7.1 迪米特原则要点

  1. 降低耦合性:迪米特原则鼓励将关注点分离,使得类之间的依赖关系尽可能松散。一个类应该尽量减少对其他类的直接依赖,而是通过中间类或接口进行通信。
  2. 提高模块独立性:迪米特原则鼓励将系统划分为独立的模块,每个模块只与其直接的朋友(直接依赖的类)进行通信。使得模块更加独立,修改一个模块不会对其他模块造成影响,提高了系统的可扩展性和可重用性。
  3. 保护隐私信息:迪米特原则要求一个对象只与其直接的朋友进行通信,不要暴露过多的内部信息给外部对象。这样可以保护对象的隐私信息,减少不必要的依赖和耦合。
  4. 提高代码的可维护性:通过遵循迪米特原则,代码的结构更加清晰,类之间的关系更加简单明了。这使得代码更易于理解、调试和修改,提高了代码的可维护性。

不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。迪米特法则是希望减少类之间的耦合,让类越独立越好。每个类都应该少了解系统的其他部分。一旦发生变化,需要了解这一变化的类就会比较少。

7.2 迪米特原则优缺点

优点

  1. 降低类之间的耦合度,提高了模块的相对独立性
  2. 耦合度降低,从而提高了类的可重用率和系统的扩展性

缺点

  1. 过度使用迪米特原则,会产生大量的中介类,导致系统的复杂度提高。在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

7.3 迪米特设计体现

广义的迪米特法则在类的设计上的体现:

  • 优先考虑将一个类设置成不变类。
  • 尽量降低一个类的访问权限。
  • 谨慎使用Serializable。
  • 尽量降低成员的访问权限。

7.4 思考迪米特原则场景

有几个设计模式在实现中使用了迪米特原则,包括:

  1. 外观模式(Facade Pattern):通过提供一个简化的接口,将复杂子系统的接口与客户端解耦,符合迪米特原则的要求,使得客户端不需要了解子系统的内部细节。
  2. 中介者模式(Mediator Pattern):通过引入一个中介者对象,将多个对象之间的通信集中处理,减少对象之间的直接交互,符合迪米特原则的要求。
  3. 迭代器模式(Iterator Pattern):通过提供一个统一的迭代接口,隐藏集合对象的内部结构,使得客户端不需要了解集合的具体实现细节,符合迪米特原则的要求。

这些设计模式都通过减少对象之间的直接依赖关系,降低耦合度,提高系统的可维护性和灵活性,从而符合迪米特原则的设计原则。

08.迪米特原则的总结

  1. 迪米特问题思考:什么是迪米特原则?什么是“高内聚、松耦合”?如何利用迪米特法则来实现“高内聚、松耦合”?
  2. 为何要用迪米特原则:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。这个时候要降低类之间耦合!
  3. 如何理解迪米特原则:是一种设计原则,指导软件模块之间的松耦合,即一个对象应该尽可能少地了解其他对象的内部细节。
  4. 如何理解内聚和耦合关系:“高内聚”有助于“松耦合”,同理,“低内聚”也会导致“紧耦合”。
  5. 什么叫做高内聚:指相近的功能应该放到同一个类中,不相近的功能不要放到同一类中。相近的功能往往会被同时修改,放到同一个类中,修改会比较集中。
  6. 什么叫做松耦合:类与类之间的依赖关系简单清晰。即使两个类有依赖关系,一个类的代码改动也不会或者很少导致依赖类的代码改动。
  7. 迪米特原则核心思想:类之间的解耦合,尽量做到弱耦合,耦合程度越低就说明类复用率才能提高。
  8. 迪米特原则要点:1.降低耦合性;2.提高模块独立性;3.保护隐私信息;4.提高代码可维护性。
  9. 迪米特原则的缺点:过度使用迪米特原则,会产生大量的中介类,导致系统的复杂度提高。在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。
  10. 思考迪米特原则场景有哪些:比如外观模式通过简化接口使复杂子系统和客户端解耦合;迭代器模式通过迭代接口隐藏集合对象内部结构让客户端不需要了解具体细节就可以遍历。他们都符合迪米特原则。

09.更多内容推荐

模块 描述 备注
GitHub 多个YC系列开源项目,包含Android组件库,以及多个案例 GitHub
博客汇总 汇聚Java,Android,C/C++,网络协议,算法,编程总结等 YCBlogs
设计模式 六大设计原则,23种设计模式,设计模式案例,面向对象思想 设计模式
Java进阶 数据设计和原理,面向对象核心思想,IO,异常,线程和并发,JVM Java高级
网络协议 网络实际案例,网络原理和分层,Https,网络请求,故障排查 网络协议
计算机原理 计算机组成结构,框架,存储器,CPU设计,内存设计,指令编程原理,异常处理机制,IO操作和原理 计算机基础
学习C编程 C语言入门级别系统全面的学习教程,学习三到四个综合案例 C编程
C++编程 C++语言入门级别系统全面的教学教程,并发编程,核心原理 C++编程
算法实践 专栏,数组,链表,栈,队列,树,哈希,递归,查找,排序等 Leetcode
Android 基础入门,开源库解读,性能优化,Framework,方案设计 Android

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/901658.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

day:25 selenium之介绍和环境配置

一、selenium介绍 selenium是一个第三方库,python有很多库; 1、什么是ui自动化? 通过模拟手工操作用户ui页面的方式,用代码去实现自动化操作和验证的行为。 2、ui自动化的优点? (1)解决重复性的功能测试和验证 (2)减少测试人员在回归测试时用例漏测和验证点的漏测 (3…

[算法学习记录] [更新中]最短路

顾名思义,最短路算法,就是求一个图中所有的点距某一个点的最短距离,常见的有Dijkstra算法、Bellman-Ford算法、Johnson算法与Floyd算法。 Dijkstra Dijistra算法实际上是一种由贪心与动态规划结合的算法,我们每次都贪心地选择到某个点的最近距离,又动态地更新着距离数组d的…

day6-static静态变量

静态变量/静态方法 被static修饰 工具类静态方法不能调用非静态。 非静态可以访问所有。 继承一个子类只能继承一个父类,可以多层继承,最大的父类为Objectcoding 练习:更好的方法:双指针不浪费空间

SpringBoot引入JWT实现用户校验

一JWT JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境间安全地传输信息。这些信息以JSON对象的形式存储在令牌中,并且可以被签名和加密。JWT通常用于身份验证和信息交换主要用途 身份验证:当用户登录成功后,服务器会生成一个JWT并返回给客户端。之后…

mcp-playwright测评

mcp-playwright介绍 mcp-playwright是一个使用 Playwright 提供浏览器自动化能力的模型上下文协议服务器。该服务器使 LLMs 能够与网页交互、截屏,并在真实的浏览器环境中执行 JavaScript。 GitHub地址:https://github.com/executeautomation/mcp-playwright。安装 npm insta…

传媒行业项目管理全解析:日事清在流程、文件、数据与风控中的一体化应用

一、关于森可文化 森可文化传媒有限公司(Senke Vision)占据了超过2800平方米的宽敞办公及拍摄场地,与众多内衣及服饰行业的上市巨头建立了不可动摇的深度合作关系。 Senke Vision汇聚了国内外顶尖的策划、设计、摄影摄像、服装搭配、化妆及品牌视觉顾问,形成了一支独一无二…

在 .NET 项目中使用 husky 完成 pre-commit 操作

将 husky 引入 .NET 项目Husky 是一个用于 Git 仓库的工具,主要用于管理 Git 钩子(hooks)。它可以帮助开发者在特定的 Git 操作(如提交、推送等)之前或之后执行自定义的脚本或命令,从而提高代码质量和团队协作效率。 主要用在前端项目中,可以通过 Husky.Net,将 Husky 的…

贸易企业数字化转型案例:基于日事清的目标管理、任务协作与流程可视化绩效优化实践

这家贸易公司如何提升内部协同效率?一、基本情况 所属行业:传统贸易行业 业务类型:国内贸易、货物及技术进出口 行业地位:拥有自己的研发人员,具备一站式解决方案能力。 合作概要:为解决组织提效,目标体系、协同体系、绩效体系的管理问题,客户与日事清达成合作,并将日…

使用nvm管理node.js版本

1.情景展示如上图所示,项目某个模块支持的node.js最高版本是17,我用的是20,所以只能降级。 2.具体分析 我现在把node.js降到16,那后续如果再需要20呢?能不能实现版本的随时切换? 3.安装nvm 我们可以使用nvm来管理node.js的版本。 下载nvm windows下载地址:https://githu…

3.19 学习记录

完成了仓库管理系统的制作,基本无bug,所展示功能都可以实现,基于 springboot 和 vue3 具体实现如下: 登录管理员页面 有仓库管理,物资管理,库存管理和统计功能仓库工作人员页面

网站测速——提升用户体验的关键

在互联网飞速发展的今天,网站已成为企业展示形象、提供服务以及用户获取信息的重要平台。而网站的速度,如同高速公路的路况,直接影响着用户的访问体验和满意度。因此,网站测速成为了网站运营和维护中不可或缺的关键环节。 网站速度对企业来说真的那么重要吗?​ 网站测速的…

表单和载荷的区别,以及python和js在处理json时的空格问题。

1、在 传载荷的时候,用json= 这样传底层会调用json.dumps来转换,如:response = requests.post(url,headers=headers,params=params,data=json.dumps(data.separators=(,":")) 2、在 传表单的时候,用data= 如:response = requests.post(url,headers=headers,…