设计模式之美
王争
前 Google 工程师,《数据结构与算法之美》专栏作者
123425 人已学习
新⼈⾸单¥98
登录后,你可以任选6讲全文学习
课程目录
已完结/共 113 讲
设计模式与范式:行为型 (18讲)
设计模式之美
15
15
1.0x
00:00/00:00
登录|注册

22 | 理论八:如何用迪米特法则(LOD)实现“高内聚、松耦合”?

一个类的代码改动不会导致依赖类的代码改动
类与类之间的依赖关系简单清晰
有助于代码修改集中,提高维护性
相近的功能放到同一个类中
总结“高内聚、松耦合”“单一职责原则”“接口隔离原则”“基于接口而非实现编程”“迪米特法则”的区别和联系
如何理解“迪米特法则”
如何理解“高内聚、松耦合”
辩证思考与灵活应用
引入两个接口Serializable和Deserializable
拆分Serialization类为Serializer和Deserializer
重构Document类
重构HtmlDownloader类
重构NetworkTransporter类
有依赖关系的类之间,尽量只依赖必要的接口
不该有直接依赖关系的类之间,不要有依赖
每个模块只应该了解与它关系密切的模块的有限知识
松耦合
高内聚
课堂讨论
重点回顾
代码实战二
代码实战一
“迪米特法则”理论描述
何为“高内聚、松耦合”
如何用迪米特法则实现“高内聚、松耦合”

该思维导图由 AI 生成,仅供参考

今天,我们讲最后一个设计原则:迪米特法则。尽管它不像 SOLID、KISS、DRY 原则那样,人尽皆知,但它却非常实用。利用这个原则,能够帮我们实现代码的“高内聚、松耦合”。今天,我们就围绕下面几个问题,并结合两个代码实战案例,来深入地学习这个法则。
什么是“高内聚、松耦合”?
如何利用迪米特法则来实现“高内聚、松耦合”?
有哪些代码设计是明显违背迪米特法则的?对此又该如何重构?
话不多说,让我们开始今天的学习吧!

何为“高内聚、松耦合”?

“高内聚、松耦合”是一个非常重要的设计思想,能够有效地提高代码的可读性和可维护性,缩小功能改动导致的代码改动范围。实际上,在前面的章节中,我们已经多次提到过这个设计思想。很多设计原则都以实现代码的“高内聚、松耦合”为目的,比如单一职责原则、基于接口而非实现编程等。
实际上,“高内聚、松耦合”是一个比较通用的设计思想,可以用来指导不同粒度代码的设计与开发,比如系统、模块、类,甚至是函数,也可以应用到不同的开发场景中,比如微服务、框架、组件、类库等。为了方便我讲解,接下来我以“类”作为这个设计思想的应用对象来展开讲解,其他应用场景你可以自行类比。
在这个设计思想中,“高内聚”用来指导类本身的设计,“松耦合”用来指导类与类之间依赖关系的设计。不过,这两者并非完全独立不相干。高内聚有助于松耦合,松耦合又需要高内聚的支持。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

迪米特法则是一个重要的设计原则,旨在实现代码的“高内聚、松耦合”。文章首先介绍了“高内聚、松耦合”设计思想的重要性和实际应用,强调了高内聚有助于松耦合,而低内聚则会导致紧耦合。接着,文章详细解释了迪米特法则的理论描述,即每个模块(类)只应该了解与之关系密切的模块的有限知识,或者说每个模块只和自己的朋友“说话”,不和陌生人“说话”。迪米特法则的核心是避免直接依赖关系的类之间存在依赖,尽量只依赖必要的接口,以实现代码的松耦合。文章还提供了两个代码实战案例,以帮助读者更好地理解迪米特法则的应用。 第一个案例展示了如何根据迪米特法则重构代码,避免直接依赖关系的类之间存在依赖,以实现松耦合。通过对NetworkTransporter、HtmlDownloader和Document类的重构,读者可以清晰地了解如何应用迪米特法则来改善代码设计,提高代码的可维护性和可测试性。 第二个案例则展示了如何根据迪米特法则的后半部分原则,即“有依赖关系的类之间,尽量只依赖必要的接口”,来优化代码设计。文章通过拆分Serialization类为Serializer和Deserializer类,并引入接口的方式,解决了迪米特法则和高内聚原则之间的矛盾,使得代码更加灵活和可维护。 通过本文的学习,读者可以深入了解迪米特法则的原理和实际应用,从而在实际开发中实现“高内聚、松耦合”的代码设计目标。文章还提到了“高内聚、松耦合”“单一职责原则”“接口隔离原则”“基于接口而非实现编程”“迪米特法则”,这些设计原则之间的区别和联系,帮助读者更好地理解和应用这些原则。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》
新⼈⾸单¥98
立即购买
登录 后留言

全部留言(126)

  • 最新
  • 精选
  • 王大喵
    联系: “接口隔离原则”是客户端不应该被强迫依赖不需要的接口,和“迪米特法则”中的有限知识异曲同工,接口簇会更加“单一职责”实现方式“基于接口而非实现编程”,达到的目的是高内聚,松耦合。 区别: 1. 各种原则最终的目的是为了实现“高内聚、松耦合”。 2. 单一职责原则 主要是指导类和模块,避免大而全,提高内聚性。 3. 接口隔离和迪米特(最小知识)主要指导“松耦合”,解耦使用方的依赖。 4. 基于接口而非实现编程:主要是解耦接口和实现,是指导思想,提高扩展性。

    作者回复: ������

    2020-11-16
    2
    14
  • 提姆
    老师您好,请问一下Document的修改为什么会使用到Factory的方式去产生?我对这一步的修改没有很大的感受,我这里的认知是Document只是单纯对文件的操作,那是不是可以透过HtmlDownloader实现相关取得文件的接口,像是IDownloader之类的名称,并直接回传Document这个类(亦或者对此类做其它的延伸),未来也可以实现其它像是json等其他格式的downloader,不知道我这个想法是不是可行?

    作者回复: 你的设计也可以的。设计本身没有最优解,合理、能自圆其说就可以了。总体上来讲,我这个例子是为了展示尽量减少类之间的耦合。

    2020-07-07
    2
    3
  • 戒惜舍得
    相近的功能。 怎么算相近啊。学晕了。

    作者回复: 咋这么能抬杠呢 你说我问你怎么相近 你能回答上来吗 这个也没法量化 设计模式不是算法 什么都能定量 更多的是教你思想 你自己去感受

    2019-12-27
    2
    3
  • 大方方
    我想知道假如 Public class NetworkTransporter { // 省略属性和其他方法... public Byte[] send(HtmlRequest htmlRequest) { //... }} 中的send 方法,必须需要HtmelRequest 才能实现功能呢? 老师修改后,参数上看起来是不依赖外部对象了,但是在很多其他实际操作时,很有可能还是需要用到外部对象来解决问题。 这种情况是不是需要做类扩展,在扩展中再具体引用HtmlRequest ?

    作者回复: “很有可能还是需要用到外部对象来解决问题”能举个例子吗? “ 这种情况是不是需要做类扩展,在扩展中再具体引用HtmlRequest” 也可以直接改这个类~,不过还是没太懂你的意思~

    2020-06-29
    2
  • 斐波那契
    老师 我有一个问题:在项目开发中,我写了一个类A 里面定义了一个方法,后来又写了一个类B 发现在B里面要用到A里面定义的那个方法(基本上一模一样),但是A跟B本身是两个不相关的类 这个时候要怎么解决? PS:这个方法不能算作是工具类,只是一段数据集的处理逻辑 那是否是搞一个抽象类 然后让A和B继承这个抽象类 把那个方法写进抽象类里?

    作者回复: 使用组合能不能解决复用问题呢

    2019-12-29
    2
  • prowu
    一直有一个消息结构与程序内部数据结构取舍的问题:程序内部是否直接复用消息协议的结构?比如:通讯消息使用的是protobuf协议,那程序内部的逻辑是直接使用protobuf的数据结构,还是自己在定义一套结构体?如果直接使用protobuf协议,那程序就紧耦合于协议了(这边就是与protobuf绑在一起了),如果自己在定义一套结构体,那就要多一层协议与内部结构的转换。

    作者回复: 这个也要具体问题具体分析 跟我们讲的vo bo很像

    2019-12-28
    2
  • 知行合一
    目的都是实现高内聚低耦合,但是出发的角度不一样,单一职责是从自身提供的功能出发,迪米特法则是从关系出发,针对接口而非实现编程是使用者的角度,殊途同归。
    2019-12-23
    9
    383
  • 下雨天
    1.单一职责原则 适用对象:模块,类,接口 侧重点:高内聚,低耦合 思考角度:自身 2.接口隔离原则 适用对象:接口,函数 侧重点:低耦合 思考角度:调用者 3.基于接口而非实现编程 适用对象:接口,抽象类 侧重点:低耦合 思考角度:调用者 4.迪米特法则 适用对象:模块,类 侧重点:低耦合 思考角度:类关系
    2019-12-25
    12
    236
  • Ken张云忠
    “高内聚、松耦合”“单一职责原则”“接口隔离原则”“基于接口而非实现编程”“迪米特法则”,它们之间的区别和联系吗? 区别: 高内聚、松耦合:是一个重要的设计思想,能够有效地提高代码的可读性和可维护性,缩小功能改动导致的代码改动范围. 单一职责原则:A class or module should have a single reponsibility.提供的功能上要单一. 接口隔离原则:Clients should not be forced to depend upon interfaces that they do not use.与外部关系上只依赖需要的抽象. 基于接口而非实现编程:Program to an interface, not an implementation.是一条比较抽象、泛化的设计思想,为了提高代码的灵活性/扩展性/可维护性. 迪米特法则: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.每个单元只该依赖与它关系密切的单元,最少知道,只与关系密切的单一交互. 联系: 职责越单一越容易做到接口隔离,也越容易做到最少知道的迪米特法则. 基于抽象编程抽象的知识越顶层越脱离具体实现,相对知道的内容就越少,也容易实现迪米特法则. 接口隔离原则与迪米特法则都强调只依赖需要的部分,接口隔离原则是相对偏上层来说的,迪米特法则是相对偏具体实现来说的. 单一职责原则/接口隔离原则/基于接口而非实现编程/迪米特法则都以实现代码的"高内聚、松耦合"为目的,提高代码的可读性和可维护性,缩小功能改动导致的代码改动范围,降低风险.
    2019-12-23
    58
  • 辣么大
    关于LoD,请记住一条:方法中不要使用ChainMethods。 坏的实践: Amount = customer.orders().last().totals().amount() 和 orders = customer.orders() lastOders = orders.last() totals = lastOders.totals() amount = totals.amount() 上面的例子中,chain中的方法改变会影响很多地方。这里注意区别建造者模式和pipeline管道,这两种的chain中的方法不易改变。 出现这样的代码,需要考虑可能是设计或实现出了问题。 LoD如何使用: 一个类C中的方法只能调用: 1、C中其他实例方法 2、它自己的参数方法 3、它创建对象的方法 4、不要调用全局变量(包括可变对象、可变单例) 例如: class HtmlDownloader{ Html html; public void downloadHtml(Transporter trans, String url){ if(checkUrl(url)){// ok 自己的实例方法 // return } rawData = trans.send(uri);// ok 参数对象的方法 Html html = createHtml(rawData); // ok 它创建的对象 html.save();// ok  它创建对象的方法 ) private boolean checkUrl(String url){ // check } } 参考: The Pragmatic Programmer 1st edition and 2nd edition
    2019-12-23
    24
    41
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部