06.迪米特法则(Demeter Principle)

明 嘉靖四十年 江南织造总局
小黄门唯唯诺诺的听完了镇守太监杨金水的训斥,赶忙回答:“知道了,干爹!”
“知道什么?!!”
杨金水打断了他的话,眼神突然变得凌厉起来:
“有些事情,不上秤没有四两重,上秤了,一千斤都挡不住。”

一言

迪米特法则,即最少知道原则。其它类的方法你想怎么实现都可以,不要在我引用的地方乱显摆。


概述

哲学是最接近世界本质的学科。理科生应该经常读到这样的论断:“生物的本质是化学,化学的本质是物理,物理的本质是数学,数学的本质是哲学。”
的确,从表现到反应、到作用、最后又到了关系。随着年龄的增长和阅历的积累,我们会越来越明晰的发现,无论是在软件设计还是日常生活,你能把事物关系处理的越好,整个体系就会越稳健。编程如此,人生亦然。

《哈利波特》中多方势力的角逐可以说精彩非凡。霍格沃兹、魔法部、食死徒、凤凰社…每个角色都给人留下了深刻的印象。错综复杂的关系使得正与邪之间的斗争变得充满变数。比如在小说中,魔法部名为司法部门,其中成员却鱼龙混杂,既有像亚瑟·韦斯莱一样心怀正义的凤凰社成员,也有背弃初心屈服于伏地魔的食死徒。
在这里插入图片描述

当面对复杂的对象关系,我们往往很难意识到无规则调用带来的风险。迪米特法则正是用于规范对象关系的一句箴言。它要求一个对象应该对其它对象保持最少的了解,也就是我们常说的低耦合,类与类之间的关系越密切,耦合度也就越大。
说到这里大家也许会想起我之前在说OCP原则时举得例子,工具方法无差别的被几百个应用模块引用是否合理?

我们不妨再进一步简化它的定义:每个对象应该只与他的直接朋友(建立关系)通信。

什么是直接朋友

每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。而耦合的方式很多:依赖,关联,组合,聚合等都是耦合关系。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。

也就是说,陌生的类最好不要以局部变量的形式出现在类的内部


欢迎来到魔法世界

我觉得说了上面一大通,太抽象了。按我的风格,我们还是从一个有趣的情景出发来拆解下迪米特法则到底是个什么东西。

不好,他们抓住了韦斯莱

在这里插入图片描述

魔法部被邪恶势力食死徒完全的渗透了,现在他们想要得到凤凰社所有成员的名单和这些凤凰社成员的秘密。食死徒们想了一个堂而皇之的借口,成立魔法部成员管理委员会,核查所有魔法部成员的名单,同时核查凤凰社成员的名单。
对于凤凰社来说,被食死徒们知道了名单并不可怕,重要的是不能泄露凤凰社成员的秘密。
亚瑟·韦斯莱既是凤凰社成员又是魔法部成员,自然成为了食死徒的首要目标。
卑鄙的食死徒终于还是对亚瑟韦斯莱用了吐真剂。

public class MajicWorld {
    public static void main(String[] args) {
        MinistryOfMagicManager touch = new MinistryOfMagicManager();
        touch.printAllGuys(new ArthurWeasley());
    }

    public static List<PhoenixSociety> initPs(){
        List<PhoenixSociety> psList = new ArrayList<>();
        PhoenixSociety p1 = new PhoenixSociety();
        p1.setName("小天狼星 布莱克");
        p1.setSecret("凤凰社的人我都认识");
        psList.add(p1);
        PhoenixSociety p2 = new PhoenixSociety();
        p2.setName("莱姆斯 卢平");
        p2.setSecret("我可以变成狼人");
        psList.add(p2);
        PhoenixSociety p3 = new PhoenixSociety();
        p3.setName("西弗勒斯 斯内普");
        p3.setSecret("我深爱着莉莉,她的儿子是我的软肋");
        psList.add(p3);
        PhoenixSociety p4 = new PhoenixSociety();
        p4.setName("尼法朵拉 唐克斯");
        p4.setSecret("死亡圣器的秘密可能是真的");
        psList.add(p4);
        return psList;
    }

    public static List<MinistryOfMagic> initMajicMembers(){
        List<MinistryOfMagic> psList = new ArrayList<>();
        MinistryOfMagic p1 = new MinistryOfMagic();
        p1.setName("尤里克·甘普");
        psList.add(p1);
        MinistryOfMagic p2 = new MinistryOfMagic();
        p2.setName("巴蒂·克劳奇");;
        psList.add(p2);
        MinistryOfMagic p3 = new MinistryOfMagic();
        p3.setName("康奈利·福吉");
        psList.add(p3);
        MinistryOfMagic p4 = new MinistryOfMagic();
        p4.setName("卢多·巴格曼");
        psList.add(p4);
        return psList;
    }
}

//魔法部成员
class MinistryOfMagic{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

//凤凰社成员
class PhoenixSociety{
    private String name;
    private String secret;//秘密

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSecret() {
        return secret;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }
}

//亚瑟韦斯莱(罗恩的爸爸),他知道部分凤凰社成员的名单
class ArthurWeasley{
    public List<PhoenixSociety> sayMembersOfPhoenixSociety(){
        List<PhoenixSociety> psList = MajicWorld.initPs();
        return psList;
    }
}

//魔法部成员管理委员会
//这个部门应该只可以管理魔法部成员,以及亚瑟韦斯莱,因为亚瑟既是魔法部成员也是凤凰社成员
//但是凤凰社成员直接暴露在魔法部是很危险的
class MinistryOfMagicManager{
    //返回魔法部的所有成员
    public List<MinistryOfMagic> getList(){
        return MajicWorld.initMajicMembers();
    }

    //输出魔法部成员和凤凰社成员
    public void printAllGuys(ArthurWeasley arthur){
        //给韦斯莱灌了吐真剂
        List<PhoenixSociety> pss = arthur.sayMembersOfPhoenixSociety();
        pss.forEach(e-> System.out.println("我是凤凰社成员:"+e.getName()+",我的秘密是:"+e.getSecret()));

        System.out.println("-------------------------------------");
        //获取全部魔法部成员
        List<MinistryOfMagic> list = this.getList();
        list.forEach(e-> System.out.println("魔法部成员:"+e.getName()));
    }
}

下面我们来分析一下这段代码的问题,
魔法部成员管理委员会类(MinistryOfMagicManager)这个类的直接朋友应该是 魔法部成员类(MinistryOfMagic )和 亚瑟·韦斯莱类(ArthurWeasley),因为只有这两个类属于魔法部,而凤凰社成员并不都是魔法部的,所以凤凰社成员类(PhoenixSociety)并不应该出现在这个类中,这违反了迪米特法则。
因为对于亚瑟·韦斯莱(ArthurWeasley)这个类的设计缺陷,直接导致了食死徒可以通过魔法部成员管理委员会类(MinistryOfMagicManager)这个类获得凤凰社成员类(PhoenixSociety)的实现细节,进而盗取出凤凰社的秘密。
在这里插入图片描述

韦斯莱是个意志坚定的人,他不会出卖我们

在这里插入图片描述

我们已经看到了,问题出在了亚瑟·韦斯莱(ArthurWeasley)这个类的设计上。只要让他遵循迪米特法则,处理好实现细节就能规避掉耦合度提高的问题。

//亚瑟韦斯莱(罗恩的爸爸),他知道部分凤凰社成员的名单
class ArthurWeasley{
    private List<PhoenixSociety> sayMembersOfPhoenixSociety(){
        List<PhoenixSociety> psList = MajicWorldUnderDemeterPrinciple.initPs();
        return psList;
    }

    public void printPs(){
        List<PhoenixSociety> phoenixSocieties = this.sayMembersOfPhoenixSociety();
        phoenixSocieties.forEach(e-> System.out.println("我是凤凰社成员:"+e.getName()+"。罪恶的食死徒,即使你对韦斯莱用了吐真剂也别想知道我的秘密。"));
    }
}

而当魔法部成员管理委员会类(MinistryOfMagicManager)再一次对韦斯莱用“吐真剂”时,由于韦斯莱将凤凰社成员类(PhoenixSociety)的引用完全私有,外部调用即便想要盗取凤凰社成员类(PhoenixSociety)的实现细节也无从下手。他们只能通过公共的方法printPs获得名单,而对于秘密则还是一无所知。

class MinistryOfMagicManager{
    //返回魔法部的所有成员
    public List<MinistryOfMagic> getList(){
        return MajicWorldUnderDemeterPrinciple.initMajicMembers();
    }

    //输出魔法部成员和凤凰社成员
    public void printAllGuys(ArthurWeasley arthur){
        //给韦斯莱灌了吐真剂
        arthur.printPs();

        System.out.println("-------------------------------------");
        //获取全部魔法部成员
        List<MinistryOfMagic> list = this.getList();
        list.forEach(e-> System.out.println("魔法部成员:"+e.getName()));

    }
}

在这里插入图片描述
我们通过迪米特法则的优化设计挫败了食死徒的阴谋。


从设计的角度来看,迪米特法则的核心是降低类与类之间的耦合,减少了不必要的依赖。但是需要注意的是,迪米特法则只是要求降低类间(对象间)耦合关系,并不是要求完全没有依赖关系。这其实是需要根据业务慎重考量的部分。
比如,我现在针对上面的实现加一个需求,编写一个凤凰社管理办公室类,用来记录所有凤凰社成员的秘密(field:secret),这个类是不是就必须要有相应的耦合关系呢?
软件设计的几个原则在实际应用中如何权衡,也是个哲学问题。


关注我,共同进步,每周至少一更。——Wayne

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

椋鸟C语言笔记#26:数据在内存中的存储(大小端字节序)、浮点数的存储(IEEE754)

萌新的学习笔记&#xff0c;写错了恳请斧正。 目录 大小端字节序 什么是大小端 写一个判断大小端的程序 浮点数在内存中的存储&#xff08;IEEE 754规则&#xff09; 引入 存储规则解释 读取规则解释 1.阶码不全为0或全为1&#xff08;规格化数&#xff09; 2.阶码全为…

鸿蒙系统走向独立,高校设立“鸿蒙班”,鸿蒙人才紧缺!

近日&#xff0c;华为以及鸿蒙系软件厂商都在积极培养鸿蒙开发人才&#xff0c;产学联动、产教融合是重要的一条路径。目前已有23家985高校、46家211高校已开设或即将开设HarmonyOS相关课程。 一位鸿蒙生态内部人士表示&#xff0c;目前鸿蒙开发人才比较紧缺&#xff0c;而安卓…

图生视频AI技术,1张图零提示词,让静态照片动起来

AI时代的发展速度比我们想象中的快多了&#xff0c;当大部分人刚学会AI生成图片时&#xff0c;现在又开始流行AI生成视频了&#xff0c;正式从图片、文字升级到短视频时代。 最近一段时间&#xff0c;AI生成视频的技术正在突飞猛进。Pika、Runway等大家熟知的海外工具都在不断…

【STM32CubeMX】F103 BxCAN

F103&BxCAN bxCAN总体描述 有一个增强的过滤机制来处理各种类型的报文此外&#xff0c;应用层任务需要更多CPU时间&#xff0c;因此报文接收所需的实时响应程度需要减轻。 接收FIFO的方案允许&#xff0c;CPU花很长时间处理应用层任务而不会丢失报文。 构筑在底层CAN驱动程…

软件设计中如何画各类图之七了解组件图:系统架构的关键视角

目录 1 前言2 组件图基本介绍3 画组件图的步骤4 组件图的用途5 场景及实际场景举例6 结语 1 前言 组件图是一种UML的图形化表示工具&#xff0c;为系统架构提供了重要视角。它描述了系统中各个组件以及它们之间的依赖关系和连接。用于展示系统中的组件、软件模块、以及它们之间…

简单实现Spring容器(五) 实现bean后置处理器BeanPostProcessor机制

阶段5: // 1.编写自己的Spring容器,实现扫描包,得到bean的class对象. // 2.扫描将 bean 信息封装到 BeanDefinition对象,并放入到Map. // 3.初始化单例池并完成getBean() createBean()方法 // 4.完成依赖注入(如果创建某个Bean对象,存在依赖注入,需要进行bean组装操作) 5.bean…

比较好的python书籍,python有什么书推荐

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;比较好的python书籍&#xff0c;python有什么书推荐&#xff0c;现在让我们一起来看看吧&#xff01; 我是在半年前接触到Python的&#xff0c;我之前没有一点编程基础&#xff0c;但在我自学的这半年里&#xff0c;我发…

绿盟 SAS堡垒机 local_user.php 权限绕过漏洞复现

绿盟 SAS堡垒机 local_user.php 权限绕过漏洞复现 一、 产品简介二、漏洞概述三、 复现环境四、漏洞复现五、小龙检测 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&…

jdom利用纯java技术对xml文档进行解析、生成、序列化等各种操作

Jdom对xml文档进行解析、生成、序列化等各种操作。 使用jdom之前&#xff0c;首先要导入jar包&#xff1a;jdom.jar 获得根元素&#xff1a; 首先确定xml文件位置 String xmlPath "./src/ceshi/Test.xml"; //使用的解析器&#xff0c;这里表示默认的解析…

资本热捧下的预制菜,如何挤出泡沫、回归务实?

在这个被快餐和即食文化主宰的时代&#xff0c;预制菜概念持续被资本热炒。 据悉&#xff0c;近30个交易日里&#xff0c;预制菜概念板块已累计上涨超15%&#xff0c;其中&#xff0c;惠发食品、得利斯、春雪食品等个股更是快速拉涨。但究竟谁才能笑到最后&#xff0c;还充满未…

数据结构和算法 - 数组

1、数组 1.1 简介 什么是数组&#xff1f; 他优缺点是什么&#xff1f;具体应用有哪些&#xff1f; 「数组 array」是一种基于顺序存储的线性数据结构&#xff0c;其将相同类型的元素存储在连续的内存空间中。我们将元素在数组中的位置称为该元素的「索引 index」。 如图&…

IDEA卡顿,进行性能优化设置(亲测有效)——情况一

需求场景 IDEA重新激活后&#xff0c;运行IDEA卡的非常卡顿&#xff0c;没有运行项目&#xff0c;CPU占比也非常高: 原因分析 可能的原因是&#xff0c;在IDEA的配置中&#xff0c;给他分配的空间比较小 解决方式 步骤一 选择顶部导航栏中的Help&#xff0c;然后点击Edi…

Java数据类型相关

数据类型 Java有哪些数据类型 定义&#xff1a;Java语言是强类型语言&#xff0c;对于每一种数据都定义了明确的具体的数据类 型&#xff0c;在内存中分配了不同大小的内存空间。 分类&#xff1a; 基本数据类型 数值型 整数类型(byte,short,int,long) 浮点类型(float,dou…

文本处理工具doctran(集成LLM和NLP库)

今天给大家推荐一款文本处理工具&#xff0c;可以Extract、Redact、Summarize、Refine、Translate、Interrogate&#xff0c;这个工具就是文档转换框架doctran。 欢迎关注公众号 doctran基于OpenAI的GPT模型和开源的NLP库来剖析文本数据。该工具也可以在LangChain框架document…

汇编指令命令

ARM汇编指令学习 工程搭建 汇编编程环境搭建 HN0AE-522LW-NNRAF-63PUS-7IGFH-YF58D 配置编译工具链 为工程配置链接脚本&#xff08;map.lds&#xff09; 将map.lds 复制到工程文件夹(在桌面创建的ARM-ASM文件) 创建汇编文件 接下来我们需要建立一个start.s汇编文件添加到我们的…

基于Microchip 光伏逆变器方案

小编杂谈新能源已经完成了至少5期的博文了&#xff0c;Boss告诉小编&#xff0c;如果还不介绍我们的产品和方案&#xff0c;黄花菜都凉了&#xff0c;所以小编这期博文就重点介绍一下Microchip在储能上的产品介绍&#xff0c;重点聊聊Microchip储能中使用的光伏逆变器的解决方案…

沉浸文旅体验!实时云渲染助力2023广东旅博会智慧文旅元宇宙体验馆上线

2023广东国际旅游产业博览会&#xff08;以下简称旅博会&#xff09;于9月15-17日在广州中国进出口商品交易会展馆A区举办&#xff0c;共设有国际展区、国内展区、智慧文旅展区、文化创意展区、旅游商品展区等五大展区&#xff0c;汇聚了来自全球近40个国家和地区的近千家参展商…

综述 2022-Genome Biology:“AI+癌症multi-omics”融合方法benchmark

Leng, Dongjin, et al. "A benchmark study of deep learning-based multi-omics data fusion methods for cancer." Genome biology 23.1 (2022): 1-32. 被引次数&#xff1a;34作者单位 红色高亮表示写论文中可以借鉴的地方 一、方法和数据集 1. 3个数据集&…

【✅如何针对大Excel做文件读取?】

✅如何针对大Excel做文件读取&#xff1f; &#x1f7e9;如何针对大Excel做文件读取&#x1f7e9;XSSFWorkbook文件读取&#x1f7e9;EasyExcel文件读取 ✅扩展知识&#x1f7e9; EasyExcel简介&#x1f7e9;EasyExcel 为什么内存占用小&#xff1f; &#x1f7e9;如何针对大Ex…

MySQL使用Not in查询效率慢的优化

引言 问题复盘&#xff0c;在查询某个数据不在另一个表中&#xff0c;查询时间非常慢&#xff0c;10几秒。究其原因not in不走索引。那么怎么解决优化呢&#xff0c;下面是简单记录。 原SQL select * from test where a not in ( select a from test2 where status in (1, 2…