在日常开发里很多代码一开始写出来也能跑但后面一改结构就很容易连锁出问题。尤其是在业务对象比较多、层级比较深的系统里经常会出现一种写法一个类为了拿到自己想要的数据层层往里钻穿透别的对象内部结构去访问“朋友的朋友”。刚开始这么写会觉得很直接甚至还挺顺手想拿城市名就order.Customer.Address.City想拿员工信息就company.Departments[i].Employees[j].Name想调底层方法就一层一层GetX().GetY().DoSomething()但这种代码有个很明显的问题你知道得太多了。一旦中间某一层结构变了外层调用代码往往也要跟着改。这类问题表面上看是“对象访问太深”本质上其实是对象之间的了解过多。对应的改进思路就是今天这篇要讲的内容迪米特法则LoD。文章目录一、原则二、解释三、什么叫“直接朋友”四、用一个生活化的比喻理解五、违反迪米特法则最明显的信号1. 大量链式调用2. 一个类知道太多别的类的细节3. 底层结构一改上层调用就跟着改4. 类和类之间的依赖关系扩散得很快六、遵守迪米特法则的好处1. 降低耦合2. 提高可维护性3. 增强封装性4. 结构更稳定七、如何尽量做到符合迪米特法则1. 只调用直接朋友的方法2. 少写链式访问3. 让对象自己处理自己的事情4. 提供必要的委托方法八、案例公司管理系统里的员工报表九、反面案例穿透式访问1. 定义员工类2. 定义部门类3. 定义公司类4. 在外部直接穿透访问这会带来什么问题十、正面案例每一层只处理自己的事情1. 员工负责提供自己的描述信息2. 部门负责汇总本部门员工信息3. 公司负责协调各部门输出4. 报表生成器只和公司打交道5. 测试代码十一、运行结果十二、这个例子里迪米特法则体现在哪里1. ReportGenerator 只认识 Company2. Company 只认识 Department3. Department 只认识 Employee4. Employee 负责描述自己十三、这种设计到底好在哪里十四、迪米特法则最容易被误解的地方十五、实际开发里怎么判断是不是违反了迪米特法则1. 这个类是不是知道了太多不该知道的内部细节2. 有没有频繁出现多层点号访问3. 中间对象结构变化时外层代码会不会大量受影响4. 当前需求是不是可以通过“委托直接朋友处理”来实现十六、总结创作权保护一、原则一个对象应当对其他对象有尽可能少的了解只与直接朋友通信不与“陌生人”说话。如果换成更直白的话可以理解成不该你知道的事情别去打听。也就是说一个对象只需要和自己直接相关的对象打交道至于更深层的内部结构不应该由它来关心。二、解释迪米特法则也叫最少知识原则Law of Demeter简称LoD。这个原则的核心非常朴素一个类知道得越少耦合通常就越低。很多系统后期难维护不是因为类太少而是因为类和类之间互相知道得太多。一个类不仅知道自己该做什么还知道别的类内部有哪些字段、有哪些集合、集合里元素又是什么结构最后牵一发而动全身。迪米特法则要解决的就是这种“过度了解”。三、什么叫“直接朋友”在迪米特法则里经常会提到一个词直接朋友。通常来说下面这些对象可以算当前对象的“直接朋友”当前对象本身this当前对象的成员变量通过构造函数传进来的对象通过方法参数传进来的对象当前对象内部创建并直接使用的对象而除此之外那些通过别的对象再拿到的对象通常就属于“陌生人”了。比如这段代码a.GetB().GetC().DoSomething();对于a来说B可能还是直接朋友但C往往已经是“朋友的朋友”了。如果你在代码里频繁这样穿透访问就很容易违反迪米特法则。四、用一个生活化的比喻理解假设你在公司里办一件事。最合理的方式通常是你联系你的对接人对接人去协调他负责的其他部门最后把结果反馈给你而不是你自己先跑到 A 部门找到 A 部门的人再让他带你找 B 部门的人再从 B 部门去找 C 部门的负责人这就是“你去打听了朋友的朋友”。从组织协作角度看这种方式效率低而且特别容易乱。从代码角度看本质上也是一样你本来只该和自己的直接对象交互却一层层钻进别人的内部结构。这就是典型的违反迪米特法则。五、违反迪米特法则最明显的信号如果你在项目里经常看到下面这些代码就该提高警惕了。1. 大量链式调用比如order.GetCustomer().GetAddress().GetCity()company.Departments[0].Employees[1].Namea.B.C.D.DoSomething()这类代码说明外部调用方已经深度依赖对象内部层次结构。2. 一个类知道太多别的类的细节比如它不仅知道公司有部门还知道部门里有员工员工里还有岗位字段岗位字段又怎么格式化显示。3. 底层结构一改上层调用就跟着改只要中间某个类的属性名、集合结构、对象层次一变外层很多地方都得一起调整。4. 类和类之间的依赖关系扩散得很快本来只该依赖Company结果最后同时依赖了Department、Employee、Address、Manager等多个间接对象。六、遵守迪米特法则的好处1. 降低耦合对象之间的依赖关系会更简单改动影响范围更可控。2. 提高可维护性每个类只需要理解自己直接协作的对象不需要理解整条对象链。3. 增强封装性内部细节被更好地封装起来外界无法轻易穿透到内部。4. 结构更稳定哪怕内部实现发生变化只要对外接口不变外部调用通常不用动。七、如何尽量做到符合迪米特法则这个原则落地时可以抓住几个很实用的习惯。1. 只调用直接朋友的方法如果你需要某个间接对象的数据不要自己一路钻进去拿。应该让你的直接朋友提供一个对外方法。2. 少写链式访问看到这种代码就该警惕a.B.C.Da.GetB().GetC().GetD()链条越长耦合通常越重。3. 让对象自己处理自己的事情谁的数据谁负责组织谁的内部结构谁负责封装外部只拿最终结果不去关心内部细节。4. 提供必要的委托方法如果外部需要某项内部信息可以在当前对象上提供一个简洁方法由当前对象去委托内部对象完成而不是让外部直接穿透进去。八、案例公司管理系统里的员工报表假设你在做一个公司管理系统。系统里有几个对象公司部门员工现在有一个需求打印某个公司中所有部门的全部员工信息。这个需求非常适合说明迪米特法则。九、反面案例穿透式访问先来看一种很常见、也很容易写出来的方式。1. 定义员工类usingSystem;publicclassEmployee{publicstringName{get;set;}publicstringPosition{get;set;}publicEmployee(stringname,stringposition){Namename;Positionposition;}}2. 定义部门类usingSystem.Collections.Generic;publicclassDepartment{publicstringDeptName{get;set;}publicListEmployeeEmployees{get;set;}publicDepartment(stringdeptName){DeptNamedeptName;EmployeesnewListEmployee();}}3. 定义公司类usingSystem.Collections.Generic;publicclassCompany{publicstringCompanyName{get;set;}publicListDepartmentDepartments{get;set;}publicCompany(stringname){CompanyNamename;DepartmentsnewListDepartment();}}4. 在外部直接穿透访问usingSystem;publicclassReportGenerator{publicvoidPrintAllEmployees(Companycompany){foreach(vardeptincompany.Departments){Console.WriteLine($部门:{dept.DeptName});foreach(varempindept.Employees){Console.WriteLine($ -{emp.Name}({emp.Position}));}}}}这段代码看起来没有问题功能也能跑。但它有一个很典型的设计问题ReportGenerator知道得太多了。它不仅知道Company里有DepartmentsDepartment里有EmployeesEmployee里有Name和Position而且还直接依赖了这一整套内部层次结构。这会带来什么问题只要下面任意一个地方变化ReportGenerator都要跟着改Departments改成别的集合结构Employees不再直接暴露Position改名员工显示逻辑改成“姓名 工号 岗位”这说明ReportGenerator和太多“陌生人”建立了联系。十、正面案例每一层只处理自己的事情更合理的方式是让每个对象只负责自己的信息组织由外层对象协调内层对象而不是让最外层一路穿透到底。1. 员工负责提供自己的描述信息publicclassEmployee{publicstringName{get;}publicstringPosition{get;}publicEmployee(stringname,stringposition){Namename;Positionposition;}publicstringGetInfo(){return${Name}({Position});}}这里的重点是员工怎么展示自己由员工类自己决定外部不需要自己去拼接Name和Position这样如果以后展示格式改了只改GetInfo()就可以。2. 部门负责汇总本部门员工信息usingSystem;usingSystem.Collections.Generic;publicclassDepartment{publicstringDeptName{get;}privatereadonlyListEmployee_employees;publicDepartment(stringdeptName){DeptNamedeptName;_employeesnewListEmployee();}publicvoidAddEmployee(Employeeemployee){_employees.Add(employee);}publicvoidPrintEmployees(){Console.WriteLine($部门:{DeptName});foreach(varempin_employees){Console.WriteLine($ -{emp.GetInfo()});}}}这里部门只关心两件事自己叫什么自己有哪些员工至于员工信息怎么描述由员工自己处理。3. 公司负责协调各部门输出usingSystem;usingSystem.Collections.Generic;publicclassCompany{publicstringCompanyName{get;}privatereadonlyListDepartment_departments;publicCompany(stringname){CompanyNamename;_departmentsnewListDepartment();}publicvoidAddDepartment(Departmentdepartment){_departments.Add(department);}publicvoidPrintAllEmployees(){Console.WriteLine(${CompanyName}员工花名册 );Console.WriteLine();foreach(vardeptin_departments){dept.PrintEmployees();Console.WriteLine();}}}这里公司也只做自己该做的事管理部门集合通知每个部门打印自己的员工信息它不会跑到部门内部去操作员工列表。4. 报表生成器只和公司打交道publicclassReportGenerator{publicvoidGenerateReport(Companycompany){company.PrintAllEmployees();}}这个版本就很干净了。ReportGenerator只认识Company并不关心公司里有多少部门部门里有多少员工员工信息到底怎么组织这就是迪米特法则想要的效果外部只和自己的直接朋友通信。5. 测试代码classProgram{staticvoidMain(){varcompanynewCompany(Leon科技有限公司);vardevDeptnewDepartment(研发部);devDept.AddEmployee(newEmployee(张三,高级工程师));devDept.AddEmployee(newEmployee(李四,中级工程师));devDept.AddEmployee(newEmployee(王五,初级工程师));varsalesDeptnewDepartment(销售部);salesDept.AddEmployee(newEmployee(赵六,销售经理));salesDept.AddEmployee(newEmployee(孙七,销售代表));varhrDeptnewDepartment(人事部);hrDept.AddEmployee(newEmployee(周八,HR主管));company.AddDepartment(devDept);company.AddDepartment(salesDept);company.AddDepartment(hrDept);varreportnewReportGenerator();report.GenerateReport(company);}}十一、运行结果 Leon科技有限公司 员工花名册 部门: 研发部 - 张三 (高级工程师) - 李四 (中级工程师) - 王五 (初级工程师) 部门: 销售部 - 赵六 (销售经理) - 孙七 (销售代表) 部门: 人事部 - 周八 (HR主管)十二、这个例子里迪米特法则体现在哪里1.ReportGenerator只认识Company它不需要知道公司内部怎么维护部门也不需要知道部门里怎么维护员工。2.Company只认识Department公司只负责协调部门不直接管理员工细节。3.Department只认识Employee部门负责遍历自己的员工并调用员工提供的信息方法。4.Employee负责描述自己员工的展示格式由自己决定外部不需要拼接字段。这就形成了非常清晰的责任边界报表类管报表入口公司管部门部门管员工员工管自身信息每一层只和直接朋友交互不向下穿透。十三、这种设计到底好在哪里最直接的好处就是内部变化不容易向外扩散。比如以后你想改员工展示格式增加工号增加所属职级改成“岗位 - 姓名”的顺序只需要改Employee.GetInfo()。如果以后部门内部员工容器从ListEmployee改成别的数据结构只要Department.PrintEmployees()对外行为不变外层也不用动。这就是封装和低耦合带来的稳定性。十四、迪米特法则最容易被误解的地方有些人一提到迪米特法则就会觉得那是不是对象之间都不要调用了当然不是。迪米特法则不是让你完全不和别的对象协作而是提醒你协作要有边界不要无节制地穿透别人内部结构。也不是说链式调用一定绝对错误。比如一些 Fluent API、本身就是为了可读性而设计的链式调用那是另一回事。真正值得警惕的是这种情况链条很长每一层都暴露内部结构外层调用方必须了解整条链中间任意一层变化都会影响外层这种才是典型的 LoD 问题。十五、实际开发里怎么判断是不是违反了迪米特法则可以用几个简单的问题快速判断。1. 这个类是不是知道了太多不该知道的内部细节如果一个类不仅知道当前对象还知道它内部套了哪些对象、对象里又有什么字段那就该警惕了。2. 有没有频繁出现多层点号访问比如a.B.C.D一旦这种代码大量出现通常就说明结构穿透比较严重。3. 中间对象结构变化时外层代码会不会大量受影响如果会那就说明封装边界没有守住。4. 当前需求是不是可以通过“委托直接朋友处理”来实现如果可以就尽量不要自己一路往下拿数据。十六、总结迪米特法则可以浓缩成一句很实用的话只和直接朋友说话不要总去打听朋友的朋友。它真正解决的问题是为什么对象层级一深代码就越来越难改为什么一个内部字段改名外层很多地方都要跟着改为什么类和类之间会形成越来越复杂的耦合关系更合理的方式应该是每一层处理自己的事情对外暴露必要接口外部只依赖直接对象内部结构尽量封装起来这样代码会更稳类之间的边界也会更清楚。创作权保护本文由 [ leonkay ] 学习总结编写水平有限内容仅供参考作为个人记录使用。若有疏漏请不吝赐教。版权归作者所有未经授权禁止转载、摘编或以其他方式使用本文内容。如需合作或转载本文请联系作者获得授权。
迪米特法则(LoD):别总去打听“朋友的朋友”
在日常开发里很多代码一开始写出来也能跑但后面一改结构就很容易连锁出问题。尤其是在业务对象比较多、层级比较深的系统里经常会出现一种写法一个类为了拿到自己想要的数据层层往里钻穿透别的对象内部结构去访问“朋友的朋友”。刚开始这么写会觉得很直接甚至还挺顺手想拿城市名就order.Customer.Address.City想拿员工信息就company.Departments[i].Employees[j].Name想调底层方法就一层一层GetX().GetY().DoSomething()但这种代码有个很明显的问题你知道得太多了。一旦中间某一层结构变了外层调用代码往往也要跟着改。这类问题表面上看是“对象访问太深”本质上其实是对象之间的了解过多。对应的改进思路就是今天这篇要讲的内容迪米特法则LoD。文章目录一、原则二、解释三、什么叫“直接朋友”四、用一个生活化的比喻理解五、违反迪米特法则最明显的信号1. 大量链式调用2. 一个类知道太多别的类的细节3. 底层结构一改上层调用就跟着改4. 类和类之间的依赖关系扩散得很快六、遵守迪米特法则的好处1. 降低耦合2. 提高可维护性3. 增强封装性4. 结构更稳定七、如何尽量做到符合迪米特法则1. 只调用直接朋友的方法2. 少写链式访问3. 让对象自己处理自己的事情4. 提供必要的委托方法八、案例公司管理系统里的员工报表九、反面案例穿透式访问1. 定义员工类2. 定义部门类3. 定义公司类4. 在外部直接穿透访问这会带来什么问题十、正面案例每一层只处理自己的事情1. 员工负责提供自己的描述信息2. 部门负责汇总本部门员工信息3. 公司负责协调各部门输出4. 报表生成器只和公司打交道5. 测试代码十一、运行结果十二、这个例子里迪米特法则体现在哪里1. ReportGenerator 只认识 Company2. Company 只认识 Department3. Department 只认识 Employee4. Employee 负责描述自己十三、这种设计到底好在哪里十四、迪米特法则最容易被误解的地方十五、实际开发里怎么判断是不是违反了迪米特法则1. 这个类是不是知道了太多不该知道的内部细节2. 有没有频繁出现多层点号访问3. 中间对象结构变化时外层代码会不会大量受影响4. 当前需求是不是可以通过“委托直接朋友处理”来实现十六、总结创作权保护一、原则一个对象应当对其他对象有尽可能少的了解只与直接朋友通信不与“陌生人”说话。如果换成更直白的话可以理解成不该你知道的事情别去打听。也就是说一个对象只需要和自己直接相关的对象打交道至于更深层的内部结构不应该由它来关心。二、解释迪米特法则也叫最少知识原则Law of Demeter简称LoD。这个原则的核心非常朴素一个类知道得越少耦合通常就越低。很多系统后期难维护不是因为类太少而是因为类和类之间互相知道得太多。一个类不仅知道自己该做什么还知道别的类内部有哪些字段、有哪些集合、集合里元素又是什么结构最后牵一发而动全身。迪米特法则要解决的就是这种“过度了解”。三、什么叫“直接朋友”在迪米特法则里经常会提到一个词直接朋友。通常来说下面这些对象可以算当前对象的“直接朋友”当前对象本身this当前对象的成员变量通过构造函数传进来的对象通过方法参数传进来的对象当前对象内部创建并直接使用的对象而除此之外那些通过别的对象再拿到的对象通常就属于“陌生人”了。比如这段代码a.GetB().GetC().DoSomething();对于a来说B可能还是直接朋友但C往往已经是“朋友的朋友”了。如果你在代码里频繁这样穿透访问就很容易违反迪米特法则。四、用一个生活化的比喻理解假设你在公司里办一件事。最合理的方式通常是你联系你的对接人对接人去协调他负责的其他部门最后把结果反馈给你而不是你自己先跑到 A 部门找到 A 部门的人再让他带你找 B 部门的人再从 B 部门去找 C 部门的负责人这就是“你去打听了朋友的朋友”。从组织协作角度看这种方式效率低而且特别容易乱。从代码角度看本质上也是一样你本来只该和自己的直接对象交互却一层层钻进别人的内部结构。这就是典型的违反迪米特法则。五、违反迪米特法则最明显的信号如果你在项目里经常看到下面这些代码就该提高警惕了。1. 大量链式调用比如order.GetCustomer().GetAddress().GetCity()company.Departments[0].Employees[1].Namea.B.C.D.DoSomething()这类代码说明外部调用方已经深度依赖对象内部层次结构。2. 一个类知道太多别的类的细节比如它不仅知道公司有部门还知道部门里有员工员工里还有岗位字段岗位字段又怎么格式化显示。3. 底层结构一改上层调用就跟着改只要中间某个类的属性名、集合结构、对象层次一变外层很多地方都得一起调整。4. 类和类之间的依赖关系扩散得很快本来只该依赖Company结果最后同时依赖了Department、Employee、Address、Manager等多个间接对象。六、遵守迪米特法则的好处1. 降低耦合对象之间的依赖关系会更简单改动影响范围更可控。2. 提高可维护性每个类只需要理解自己直接协作的对象不需要理解整条对象链。3. 增强封装性内部细节被更好地封装起来外界无法轻易穿透到内部。4. 结构更稳定哪怕内部实现发生变化只要对外接口不变外部调用通常不用动。七、如何尽量做到符合迪米特法则这个原则落地时可以抓住几个很实用的习惯。1. 只调用直接朋友的方法如果你需要某个间接对象的数据不要自己一路钻进去拿。应该让你的直接朋友提供一个对外方法。2. 少写链式访问看到这种代码就该警惕a.B.C.Da.GetB().GetC().GetD()链条越长耦合通常越重。3. 让对象自己处理自己的事情谁的数据谁负责组织谁的内部结构谁负责封装外部只拿最终结果不去关心内部细节。4. 提供必要的委托方法如果外部需要某项内部信息可以在当前对象上提供一个简洁方法由当前对象去委托内部对象完成而不是让外部直接穿透进去。八、案例公司管理系统里的员工报表假设你在做一个公司管理系统。系统里有几个对象公司部门员工现在有一个需求打印某个公司中所有部门的全部员工信息。这个需求非常适合说明迪米特法则。九、反面案例穿透式访问先来看一种很常见、也很容易写出来的方式。1. 定义员工类usingSystem;publicclassEmployee{publicstringName{get;set;}publicstringPosition{get;set;}publicEmployee(stringname,stringposition){Namename;Positionposition;}}2. 定义部门类usingSystem.Collections.Generic;publicclassDepartment{publicstringDeptName{get;set;}publicListEmployeeEmployees{get;set;}publicDepartment(stringdeptName){DeptNamedeptName;EmployeesnewListEmployee();}}3. 定义公司类usingSystem.Collections.Generic;publicclassCompany{publicstringCompanyName{get;set;}publicListDepartmentDepartments{get;set;}publicCompany(stringname){CompanyNamename;DepartmentsnewListDepartment();}}4. 在外部直接穿透访问usingSystem;publicclassReportGenerator{publicvoidPrintAllEmployees(Companycompany){foreach(vardeptincompany.Departments){Console.WriteLine($部门:{dept.DeptName});foreach(varempindept.Employees){Console.WriteLine($ -{emp.Name}({emp.Position}));}}}}这段代码看起来没有问题功能也能跑。但它有一个很典型的设计问题ReportGenerator知道得太多了。它不仅知道Company里有DepartmentsDepartment里有EmployeesEmployee里有Name和Position而且还直接依赖了这一整套内部层次结构。这会带来什么问题只要下面任意一个地方变化ReportGenerator都要跟着改Departments改成别的集合结构Employees不再直接暴露Position改名员工显示逻辑改成“姓名 工号 岗位”这说明ReportGenerator和太多“陌生人”建立了联系。十、正面案例每一层只处理自己的事情更合理的方式是让每个对象只负责自己的信息组织由外层对象协调内层对象而不是让最外层一路穿透到底。1. 员工负责提供自己的描述信息publicclassEmployee{publicstringName{get;}publicstringPosition{get;}publicEmployee(stringname,stringposition){Namename;Positionposition;}publicstringGetInfo(){return${Name}({Position});}}这里的重点是员工怎么展示自己由员工类自己决定外部不需要自己去拼接Name和Position这样如果以后展示格式改了只改GetInfo()就可以。2. 部门负责汇总本部门员工信息usingSystem;usingSystem.Collections.Generic;publicclassDepartment{publicstringDeptName{get;}privatereadonlyListEmployee_employees;publicDepartment(stringdeptName){DeptNamedeptName;_employeesnewListEmployee();}publicvoidAddEmployee(Employeeemployee){_employees.Add(employee);}publicvoidPrintEmployees(){Console.WriteLine($部门:{DeptName});foreach(varempin_employees){Console.WriteLine($ -{emp.GetInfo()});}}}这里部门只关心两件事自己叫什么自己有哪些员工至于员工信息怎么描述由员工自己处理。3. 公司负责协调各部门输出usingSystem;usingSystem.Collections.Generic;publicclassCompany{publicstringCompanyName{get;}privatereadonlyListDepartment_departments;publicCompany(stringname){CompanyNamename;_departmentsnewListDepartment();}publicvoidAddDepartment(Departmentdepartment){_departments.Add(department);}publicvoidPrintAllEmployees(){Console.WriteLine(${CompanyName}员工花名册 );Console.WriteLine();foreach(vardeptin_departments){dept.PrintEmployees();Console.WriteLine();}}}这里公司也只做自己该做的事管理部门集合通知每个部门打印自己的员工信息它不会跑到部门内部去操作员工列表。4. 报表生成器只和公司打交道publicclassReportGenerator{publicvoidGenerateReport(Companycompany){company.PrintAllEmployees();}}这个版本就很干净了。ReportGenerator只认识Company并不关心公司里有多少部门部门里有多少员工员工信息到底怎么组织这就是迪米特法则想要的效果外部只和自己的直接朋友通信。5. 测试代码classProgram{staticvoidMain(){varcompanynewCompany(Leon科技有限公司);vardevDeptnewDepartment(研发部);devDept.AddEmployee(newEmployee(张三,高级工程师));devDept.AddEmployee(newEmployee(李四,中级工程师));devDept.AddEmployee(newEmployee(王五,初级工程师));varsalesDeptnewDepartment(销售部);salesDept.AddEmployee(newEmployee(赵六,销售经理));salesDept.AddEmployee(newEmployee(孙七,销售代表));varhrDeptnewDepartment(人事部);hrDept.AddEmployee(newEmployee(周八,HR主管));company.AddDepartment(devDept);company.AddDepartment(salesDept);company.AddDepartment(hrDept);varreportnewReportGenerator();report.GenerateReport(company);}}十一、运行结果 Leon科技有限公司 员工花名册 部门: 研发部 - 张三 (高级工程师) - 李四 (中级工程师) - 王五 (初级工程师) 部门: 销售部 - 赵六 (销售经理) - 孙七 (销售代表) 部门: 人事部 - 周八 (HR主管)十二、这个例子里迪米特法则体现在哪里1.ReportGenerator只认识Company它不需要知道公司内部怎么维护部门也不需要知道部门里怎么维护员工。2.Company只认识Department公司只负责协调部门不直接管理员工细节。3.Department只认识Employee部门负责遍历自己的员工并调用员工提供的信息方法。4.Employee负责描述自己员工的展示格式由自己决定外部不需要拼接字段。这就形成了非常清晰的责任边界报表类管报表入口公司管部门部门管员工员工管自身信息每一层只和直接朋友交互不向下穿透。十三、这种设计到底好在哪里最直接的好处就是内部变化不容易向外扩散。比如以后你想改员工展示格式增加工号增加所属职级改成“岗位 - 姓名”的顺序只需要改Employee.GetInfo()。如果以后部门内部员工容器从ListEmployee改成别的数据结构只要Department.PrintEmployees()对外行为不变外层也不用动。这就是封装和低耦合带来的稳定性。十四、迪米特法则最容易被误解的地方有些人一提到迪米特法则就会觉得那是不是对象之间都不要调用了当然不是。迪米特法则不是让你完全不和别的对象协作而是提醒你协作要有边界不要无节制地穿透别人内部结构。也不是说链式调用一定绝对错误。比如一些 Fluent API、本身就是为了可读性而设计的链式调用那是另一回事。真正值得警惕的是这种情况链条很长每一层都暴露内部结构外层调用方必须了解整条链中间任意一层变化都会影响外层这种才是典型的 LoD 问题。十五、实际开发里怎么判断是不是违反了迪米特法则可以用几个简单的问题快速判断。1. 这个类是不是知道了太多不该知道的内部细节如果一个类不仅知道当前对象还知道它内部套了哪些对象、对象里又有什么字段那就该警惕了。2. 有没有频繁出现多层点号访问比如a.B.C.D一旦这种代码大量出现通常就说明结构穿透比较严重。3. 中间对象结构变化时外层代码会不会大量受影响如果会那就说明封装边界没有守住。4. 当前需求是不是可以通过“委托直接朋友处理”来实现如果可以就尽量不要自己一路往下拿数据。十六、总结迪米特法则可以浓缩成一句很实用的话只和直接朋友说话不要总去打听朋友的朋友。它真正解决的问题是为什么对象层级一深代码就越来越难改为什么一个内部字段改名外层很多地方都要跟着改为什么类和类之间会形成越来越复杂的耦合关系更合理的方式应该是每一层处理自己的事情对外暴露必要接口外部只依赖直接对象内部结构尽量封装起来这样代码会更稳类之间的边界也会更清楚。创作权保护本文由 [ leonkay ] 学习总结编写水平有限内容仅供参考作为个人记录使用。若有疏漏请不吝赐教。版权归作者所有未经授权禁止转载、摘编或以其他方式使用本文内容。如需合作或转载本文请联系作者获得授权。