关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 算法设计 »设计模式原则之迪米特法则

    设计模式原则之迪米特法则

    2015-04-05 00:00:00 出处:richieyang
    分享

    迪米特法则的简写为 LoD,看清楚中间的那个 o 是小写。迪米特法则也叫做做最少知识原则(Least Knowledge Principle,简称 LKP)说的都是一会事,一个对象应该对其他对象有最少的了解,通俗的讲一 个类对自己需要耦合或者调用的类应该知道的最少,你类内部是怎么复杂、怎么的纠缠不清都和我没关系, 那是你的类内部的事情,我就知道你提供的这么多 public 方法,我就调用这个;迪米特法则包含以下四层 意思:

    只和朋友交流。迪米特还有一个英文解释叫做“Only talk to your immedate friends”,只和直接 的朋友通信,什么叫做直接的朋友呢?每个对象都必然会和其他对象有耦合关系,两个对象之间的耦合就 成为朋友关系,这种关系有很多比如组合、聚合、依赖等等。我们来说个例子说明怎么做到只和朋友交流。 说是有这么一个故事,老师想让体育委员确认一下全班女生来齐没有,就对他说:“你去把全班女生清 一下。”体育委员没听清楚,或者是当时脑子正在回忆什么东西,就问道:“亲哪个?”老师¥#……¥%。 我们来看这个笑话怎么用程序来实现,先看类图:

    设计模式原则之迪米特法则

    Teacher.java 的源程序如下:

    package com.cbf4life.common;
    import java.util.ArrayList;
    import java.util.List;
    /**
    * I'm glad to share my knowledge with you all.
    * 老师类
    */
    public class Teacher {
    //老师对学生发布命令, 清一下女生
    public void commond(GroupLeader groupLeader){
     List<Girl> listGirls = new ArrayList() ;
     //初始化女生
     for(int i=0;i<20;i++){
     listGirls.add(new Girl());
     }
     //告诉体育委员开始执行清查任务
     groupLeader.countGirls(listGirls);
     }
    }

    老师就有一个方法,发布命令给体育委员,去清查一下女生的数量。 下面是体育委员 GroupLeader.java 的源程序:

    package com.cbf4life.common;
    import java.util.List;
    /**
    * @author cbf4Life cbf4life@126.com
    * I'm glad to share my knowledge with you all.
    * 体育委员,这个太难翻译了都是中国的特色词汇
    */
    public class GroupLeader {
    //有清查女生的工作
    public void countGirls(List<Girl> listGirls){
     System. out.println(" 女生数量是: "+listGirls.size());
     }
    }

    下面是 Girl.java,就声明一个类,没有任何的代码:

    package com.cbf4life.common;
    /**
    * @author cbf4Life cbf4life@126.com
    * I'm glad to share my knowledge with you all.
    * 女生
    */
    public class Girl {
    }

    我们来看这个业务调用类 Client:

    package com.cbf4life.common;
    /**
    * @author cbf4Life cbf4life@126.com
    * I'm glad to share my knowledge with you all.
    * 我们使用Client来描绘一下这个场景
    */
    public class Client {
    public static void main(String[] args) {
     Teacher teacher= new Teacher();
     //老师发布命令
     teacher.commond(new GroupLeader());
     }
    }

    运行的结果如下:

    女生数量是: 20

    我们回过头来看这个程序有什么问题,首先来看 Teacher 有几个朋友,就一个 GroupLeader 类,这个 就是朋友类,朋友类是怎么定义的呢? 出现在成员变量、方法的输入输出参数中的类被称为成员朋友类, 迪米特法则说是一个类只和朋友类交流, 但是 commond 方法中我们与 Girl 类有了交流,声明了一个 List动态数组,也就是与一个陌生的类 Girl 有了交流,这个不好,那我们再来修改一下,类图还是不变,先修改一下 GroupLeader 类,看源码:

    package com.cbf4life.common2;
    import java.util.ArrayList;
    import java.util.List;
    /**
    * @author cbf4Life cbf4life@126.com
    * I'm glad to share my knowledge with you all.
    * 体育委员,这个太难翻译了都是中国的特色词汇
    */
    public class GroupLeader {
    //有清查女生的工作
    public void countGirls(){
     List<Girl> listGirls = new ArrayList<Girl>();
     //初始化女生
     for(int i=0;i<20;i++){
     listGirls.add(new Girl());
     }
     System. out.println(" 女生数量是: "+listGirls.size());
     }
    }

    下面是 Teacher.java 程序:

    package com.cbf4life.common2;
    /**
    * 
    * I'm glad to share my knowledge with you all.
    * 老师类
    */
    public class Teacher {
    //老师对学生发布命令, 清一下女生
    public void commond(GroupLeader groupLeader){
     //告诉体育委员开始执行清查任务
     groupLeader.countGirls();
     }
    }

    程序做了一个简单的修改,就是把 Teacher 中的对 List初始化(这个是有业务意义的,产生出 全班的所有人员)移动到了 GroupLeader 的 countGrils 方法中,避开了 Teacher 类对陌生类 Girl 的访问, 减少系统间的耦合。记住了,一个类只和朋友交流,不与陌生类交流, 不要出现 getA().getB().getC().getD() 这种情况(在一种极端的情况下是允许出现这种访问:每一个点号后面的返回类型都相同),那当然还要和 JDK API 提供的类交流,否则你想脱离编译器存在呀!

    朋友间也是有距离的。人和人之间是有距离的,太远就不是朋友了,太近就浑身不自在,这和类间关 系也是一样,即使朋友类也不能无话不说,无所不知。大家在项目中应该都碰到过这样的需求:调用一个 类,然后必须是先执行第一个方法,然后是第二个方法,根据返回结果再来看是否可以调用第三个方法, 或者第四个方法等等,我们用类图表示一下:

    设计模式原则之迪米特法则

    很简单的类图,实现软件安装过程的第一步做什么、第二步做什么、第三步做什么这样一个过程,我 们来看三个类的源代码,先看 Wizard 的源代码:

    package com.cbf4life.common3;
    import java.util.Random;
    /**
    * @author cbf4Life cbf4life@126.com
    * I'm glad to share my knowledge with you all.
    * 按照步骤执行的业务逻辑类
    */
    public class Wizard {
    private Random rand = new Random(System. currentTimeMillis());
    public int first(){
     System. out.println(" 执行第一个方法...");
     return rand.nextInt(100);
     }
    //第二步
    public int second(){
     System. out.println(" 执行第二个方法...");
     return rand.nextInt(100);
     }
    //第三个方法
    public int third(){
     System. out.println(" 执行第三个方法...");
     return rand.nextInt(100);
     }
    }

    分别定义了三个步骤方法,每个步骤中都有相关的业务逻辑完成指定的任务,我们使用一个随机函数 来代替业务执行的返回值。再来看软件安装过程 InstallSoftware 源码:

    package com.cbf4life.common3;
    /**
    * @author cbf4Life cbf4life@126.com
    * I'm glad to share my knowledge with you all.
    * 业务组装类,负责调用各个步骤
    */
    public class InstallSoftware {
    public void installWizard(Wizard wizard){
     int first = wizard.first();
     //根据first返回的结果,看是否需要执行second
     if(first>50){
     int second = wizard.second();
     if(second>50){
     int third = wizard.third();
     if(third >50){
     wizard.first();
     }
     }
    }
    }
    }

    其中 installWizard 就是一个向导式的安装步骤,我们看场景是怎么调用的:

    package com.cbf4life.common3;
    /**
    * @author cbf4Life cbf4life@126.com
    * I'm glad to share my knowledge with you all.
    * 业务场景
    */
    public class Client {
    public static void main(String[] args) {
     InstallSoftware invoker = new InstallSoftware();
     invoker.installWizard(new Wizard());
     }
    }

    这个程序很简单,运行结果和随机数有关,我就不粘贴上来了。我们想想这个程序有什么问题吗? Wizard 类把太多的方法暴露给 InstallSoftware 类了,这样耦合关系就非常紧了,我想修改一个方法的返 回值,本来是 int 的,现在修改为 boolean,你看就需要修改其他的类,这样的耦合是极度不合适的, 迪米 特法则就要求类“小气”一点,尽量不要对外公布太多的 public 方法和非静态的 public 变量, 尽量内敛, 多使用 private,package-private、protected 等访问权限。我们来修改一下类图:

    设计模式原则之迪米特法则

    我们再来看一下程序的变更,先看 Wizard 程序:

    package com.cbf4life.common4;
    import java.util.Random;
    /**
    * @author cbf4Life cbf4life@126.com
    * I'm glad to share my knowledge with you all.
    * 按照步骤执行的业务逻辑类
    */
    public class Wizard {
    private Random rand = new Random(System. currentTimeMillis());
    //第一步
    private int first(){
     System. out.println(" 执行第一个方法...");
     return rand.nextInt(100);
     }
    //第二步
    private int second(){
     System. out.println(" 执行第二个方法...");
     return rand.nextInt(100);
     }
    //第三个方法
    private int third(){
     System. out.println(" 执行第三个方法...");
     return rand.nextInt(100);
     }
    //软件安装过程
    public void installWizard(){
     int first = this.first();
     //根据first返回的结果,看是否需要执行second
     if(first>50){
     int second = this.second();
     if(second>50){
     int third = this.third();
     if(third >50){
     this.first();
     }
     }
     }
     }
    }

    三个步骤的访问权限修改为 private,同时把 installeWizad 移动的 Wizard 方法中,这样 Wizard 类就 对外只公布了一个 public 方法,类的高内聚特定显示出来了。我们再来看 InstallSoftware 源码:

    package com.cbf4life.common4;
    /**
    * @author cbf4Life cbf4life@126.com
    * I'm glad to share my knowledge with you all.
    * 业务组装类,负责调用各个步骤
    */
    public class InstallSoftware {
    public void installWizard(Wizard wizard){
     //不废话,直接调用
     wizard.installWizard();
     }
    }

    Client 类没有任何改变,就不在拷贝了,这样我们的程序就做到了弱耦合,一个类公布越多的 public 属性或方法,修改的涉及面也就越大,也就是变更引起的风险就越大。因此为了保持朋友类间的距离,你 需要做的是:减少 public 方法,多使用 private、package-private(这个就是包类型,在类、方法、变量 前不加访问权限,则默认为包类型)protected 等访问权限,减少非 static 的 public 属性,如果成员变量 或方法能加上 final 关键字就加上,不要让外部去改变它。

    是自己的就是自己的。在项目中有一些方法,放在本类中也可以,放在其他类中也没有错误,那怎么 去衡量呢?你可以坚持这样一个原则: 如果一个方法放在本类中,即不增加类间关系,也对本类不产生负 面影响,就放置在本类中。

    谨慎使用 Serializable。实话说,这个问题会很少出现的,即使出现也会马上发现问题。是怎么回事呢? 举个例子来说,如果你使用 R M I 的方式传递一个对象 VO( Va lu e Object),这个对象就必须使用 Serializable 接口,也就是把你的这个对象进行序列化,然后进行网络传输。突然有一天,客户端的 VO 对象修改了一个 属性的访问权限,从 private 变更为 public 了,如果服务器上没有做出响应的变更的话,就会报序列化失败。 这个应该属于项目管理范畴,一个类或接口客户端变更了,而服务端没有变更,那像话吗?!

    迪米特法则的核心观念就是类间解耦,弱耦合,只有弱耦合了以后,类的复用率才可以提高,其要求 的结果就是产生了大量的中转或跳转类,类只能和朋友交流,朋友少了你业务跑不起来,朋友多了,你项 目管理就复杂,大家在使用的时候做相互权衡吧。

    不知道大家有没有听过这样一个理论:“任何 2 个素不相识的人中间最多只隔着 6 个人,即只用 6 个人 就可以将他们联系在一起”,这个理论的学名叫做“六度分离”,应用到我们项目中就是说我和我要调用的 类之间最多有 6 次传递,呵呵,这只能让大家当个乐子来看,在实际项目中你跳两次才访问到一个类估计 你就会想办法了,这也是合理的,迪米特法则要求我们类间解耦,但是解耦是有限度的,除非是计算机的 最小符号二进制的 0 和 1,那才是完全解耦,我们在实际的项目中时,需要适度的考虑这个法则,别为了套 用法则而做项目,法则只是一个参考,你跳出了这个法则,也不会有人判你刑,项目也未必会失败,这就 需要大家使用的是考虑如何度量法则了。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    商业智能成CIO优先关注点 技术落地方显成效(1)

    用linux安装MySQL时产生问题破解

    JAVA中关于Map的九大问题

    windows 7旗舰版无法使用远程登录如何开启telnet服务

    Android View 事件分发机制详解

    MySQL用户变量的用法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。