草庐IT

设计模式七大原则—迪米特法则

坚持 2023-03-28 原文

1.概念

1.1.知道的越少越好

迪米特法则,结合其含义又称之为“最少知道原则”,即一个类作为一个调用方,应当对自己依赖的类(被调用的类)其中所处理的逻辑细节,知道的越少越好。对于被依赖的类(被调用的类)不管在使用上多么的复杂,它都应尽量将处理逻辑封装在它的内部,对调用方提供简洁明了的公共方法即可,以此减轻上层调用方过多承担复杂逻辑的压力和变化。

1.2.朋友和陌生人

对于程序编码设计是否遵循了“迪米特法则”,我们通常可以使用一段经典的描述来判断,该描述是:“只和朋友通信,不和陌生人说话”。那么对于这段话中什么是朋友,什么是陌生人,下面对其进行一个介绍。

每个对象都会与其他对象之间都存在一定程度的耦合关系,其中主要耦合方式包含:依赖、关联、组合、聚合等等。如果某个类作为被调用者,在其调用方的类中的内部主要体现为:类中成员变量的类型、方法参数类型、方法返回值类型,那么该类就属于调用者的“朋友”。

对于某个类而言,当它作为“被调用者”在“调用者”当中没有作为:类中成员变量的类型、方法参数类型、方法返回值类型,这些形式出现在类中,而是仅作为局部变量出现在某个方法体中,那么对于这种情况,该类就属于“陌生人”。

1.3.高内聚低耦合

如果类在编写时产生了大量与“陌生人说话”,那么这样会导致依赖的类具有隐匿性,如果“陌生人”发生改动,那么其他使用“陌生人”的类(调用方)很难精确的做出协调,需要改动的地方也很难预估,这样就加剧了类与类之间的耦合程度,你很难断绝对某个类的依赖。

另外,本来处理逻辑是类自己内部可以处理的,如果非要将处理逻辑带到上层(调用方),这样就导致上层(调用方)违背了“最少知道原则”,并且不符合高内聚(自己的事情自己做)。

由此可见,迪米特法则的核心思想就是让编码设计实现:高内聚,低耦合。


2.举例

我们的程序最终要想实现迪米特法则”,其实就是遵循“最少知道原则”,具体实现这个原则就要程序做到“只和朋友通信,不和陌生人说话”。这些介绍在理解层面上都是相对抽象的,接下来我将通过生活中的例子和代码来更通俗的体会迪米特法则。

 

2.1.生活场景

在实际的生活当中,人们都可以通过点外卖的方式来解决自己用餐的需求,接下来我将以点外卖解决用餐需求作为背景,来类比“迪米特法则”中的概念。

张三心血来潮突然想吃“全聚德”的菜肴,但是他又不想出门走上几公里的路去门店吃。于是他在网上查找发现了“全聚德”刚刚开设的外送模式,他哽咽着口水赶忙的打电话预定了外送订单。

由于“全聚德”在外送模式下刚刚开设还在起步阶段,有些外送门路还不是很了解,全聚德一开始将外送员、厨师、食材一起带到张三家,在张三家现场制作菜肴。在家中现场制作的过程中经常发生一些问题:张三在看到制作流程后经常会指手画脚导致味道“四不像”、厨师由于身体情况做了几道菜后就经常去拉肚子导致糊锅。

这些问题导致了张三并没有享受到了外送模式的便捷,因为当前的外送模式,全聚德让张三知道了太多的菜肴制作细节并且于过多的人产生联系,对于这个场景参照到“迪米特法则”来说,就是没有让使用方(张三)遵循“最少知道原则”,让使用者过多知道一些细节,从而增加了复杂程度。

另外在外送模式下,全聚德让过多的人员参与到了外送当中,其中有外送员、厨师、食材。其实张三作为顾客并不用和厨师、食材打交道,这样只会增加用餐的依赖程度,张三其实只用和餐馆的外送员打交道即可。那么这里的场景参照到“迪米特法则”当中,对于外送员而言和张三就是“朋友”,厨师、食材是“陌生人”,上面的例子中,厨师在现场制作菜肴经常因为身体情况拉肚子,就是体现出依赖了“陌生关系”,从而增加了耦合度并因此带来不可预估的风险。

“全聚德”为了实现更好的外卖用餐体验,使用迪米特法则对外送模式进行改良,之后的张三在不用关心更多的菜肴制作细节和对厨师的依赖,他只用接到外送员外送的菜肴,就可以快捷的享受到美食。

 

2.2.代码案例

在通过生活场景和迪米特法则概念进行类比后,我们接着这个例子通过代码案例在加深对迪米特法则的理解,以及使用它的好处。代码案例有两种形式,一种是没有遵循迪米特法则的例子,另一个是遵循了迪米特法则的例子,后者是针对前者存在问题改进,通过对比的形式更能突出该原则的优势。

 未使用迪米特法则

 1     class Person
 2     {
 3 
 4         public void 点外卖(外送员 foodDelivery,string 菜名)
 5         {
 6             厨师 张大师= foodDelivery.通知餐馆安排人员制作菜肴();
 7             var 菜肴=  张大师.烹饪(菜名);
 8 
 9             foodDelivery.送餐(菜肴);
10 
11             Console.WriteLine($"送餐成功,开始吃{菜肴}");
12 
13         }
14     }
15 
16     class 外送员 
17     {
18         public 厨师 通知餐馆安排人员制作菜肴()
19         {
20             return new 厨师();
21         }
22         public string 送餐(string 菜肴)
23         {
24             Console.WriteLine("根据路线进行送餐");
25             return 菜肴;
26         }
27     }
28 
29     class 厨师
30     {
31         public string 烹饪(string 菜名)
32         {
33             Console.WriteLine("烹饪制作");
34             return 菜名;
35         }
36     }

我们主要看Person类中的代码,Person类在当前代码中作为一个调用者(使用方),在点外卖的方法中使用到了两个类,一个是外送员类和厨师类。那么基于本文中对“朋友与陌生人”的概念,此时的朋友其实就是外送员类,因为它作为一个方法的参数类型包含在类中,是符合朋友的标准。对于厨师类而言就是陌生人,因为它作为局部变量在类的方法中,那么对于以上代码很显然就违反了迪米特法则。

 

使用迪米特法则

 1     class Person
 2     {
 3 
 4         public void 点外卖(外送员 foodDelivery,string 菜名)
 5         {          
 6           var 菜肴= foodDelivery.送餐(菜名);
 7             Console.WriteLine($"送餐成功,开始吃{菜肴}");
 8         }
 9     }
10 
11     class 外送员 
12     {
13         public 厨师 通知餐馆安排人员制作菜肴()
14         {
15             return new 厨师();
16         }
17         public string 送餐(string 菜名)
18         {
19             var 菜肴 =  通知餐馆安排人员制作菜肴().烹饪(菜名);
20            
21             Console.WriteLine("根据路线进行送餐");
22             return 菜肴;
23         }
24     }
25 
26     class 厨师
27     {
28         public string 烹饪(string 菜名)
29         {
30             Console.WriteLine("烹饪制作");
31             return 菜名;
32         }
33     }

通常作为顾客并不关心菜肴的制作过程或外送员送餐的路线,他只关心外送员能够在预期时间进行送餐,使他能吃上饭。所以该代码案例主要将菜肴的制作环节从Person类中取掉,将菜肴的制作推向外部,这样就做到了让Person类遵循“最少知道原则”,另外切断点外卖方法中与厨师类(陌生人)的联系,促使Person类只和“朋友通信”(外送员),这样就实现了迪米特法则。


3.补充

类与类之间的耦合是不可避免的,遵循迪米特法则并不是完全解除依赖,而是在一定程度上降低类与类之间不必要的依赖。例如其中的某个概念:“不和陌生人说话”,这就并不是一个能够完全杜绝的情况,我们只能尽量的不要让陌生的类作为局部变量出现在类的内部,并且保障类对自己依赖的类知道的越少越好。

这从侧面也反映了设计模式原则并不是一个教条,而必须遵守,作为编码者更重要的是提炼其中的思想,根据实际的情况尽量的让程序设计做到最大化的“高内聚,低耦合”。

有关设计模式七大原则—迪米特法则的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  5. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  6. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  7. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  8. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

  9. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

  10. ruby - 最佳原则中的原则 - 2

    我似乎经常遇到一些设计问题,但我不知道是什么是真的很合适。一方面我经常听到我应该限制耦合和坚持单一职责,但当我这样做时,我常常发现它很困难到在需要时将信息获取到程序的一部分。为了例如,classSingerdefinitialize(name)@name=nameendattr:nameend那么Song应该是:classSongdefnew(singer)@singer=singerendend或classSongdefnew(singer_name)@singer_name=singer_nameendend后者耦合性小,按道理应该用。但如果我以后发现宋有什么需要了解更多歌手,我的

随机推荐