Java面向对象设计原则之迪米特法则介绍

目录
  • 一、迪米特法则的定义
  • 二、迪米特法则的含义
  • 三、总结

一、迪米特法则的定义

迪米特法则,也称为最少知识原则,虽然名字不同,但描述的是同一个规则:一个对象应该对其他对象有最少的了解。通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,被耦合或调用的类的内部是如何复杂都和我没关系,我就知道你提供的这么多public方法,我就调用这么多,其他的我一概不关心。

二、迪米特法则的含义

迪米特法则对类的低耦合提出了明确的规定,其包含以下几层含义。

(一)、只和朋友交流

迪米特法则,要求只与直接朋友通信。什么叫直接朋友?每个对象都必然会与其他对象有耦合关系,两个对象之间的耦合就成为朋友关系,这种关系的类型有很多,例如组合、聚合、依赖等。下面我们举例说明如何才能做到只与直接朋友进行交流。

举例:老师让体育委员确认一下全班女生是否都到齐?类图如下图所示:

其实现过程如下代码:

老师类:

public class Teacher {
    //老师发出命令,清点一下女生人数
    public void command(GroupLeader groupLeader) {
        List<Girl> girls = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            girls.add(new Girl());
        }
        //告诉体育委员开始执行清查任务
        groupLeader.countGirls(girls);
    }
}

老师只有一个方法command,先定义出所有的女生,然后发布命令给体育委员,去清点女生的人数。

体育委员GroupLeader的代码如下:

public class GroupLeader {
    public void countGirls(List<Girl> girlList) {
        System.out.println("女生数量: " + girlList.size());
    }
}

老师类和体育委员类都对Girl类产生依赖,而且女生类不需要执行任何动作,因此定义如下:

public class Girl {
}

再定义一个场景类:

public class Client {
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        teacher.command(new GroupLeader());
    }
}

运行结果如下:

女生数量: 10

体育委员按照老师的要求对女生进行了清点,并得出了数量。我们回过头来思考一下这个程序有什么问题,首先确定Teacher类有几个朋友类,它仅有一个朋友类-------GroupLeader,为什么Girl不是朋友类呢?Teacher对Girl类产生了依赖啊,朋友类的定义是这样的:出现在成员变量、方法的输入参数、输出参数中的类成为成员朋友类。而Gril这个类是出现在command方法内部的,因此不属于Teacher的直接朋友。

迪米特法则告诉我们一个类只与朋友类交流,但是我们刚刚定义的command方法却与Girl类有了交流,声明了List<Girl>动态集合,这样就破坏了Teacher的健壮性。

问题已经发现,我们修改一下程序,将类图稍作调整,如下图所示:

修改后的老师类:

public class Teacher {
    //老师发出命令,清点一下女生人数
    public void command(GroupLeader groupLeader) {
        //告诉体育委员开始执行清查任务
        groupLeader.countGirls();
    }
}

修改后的GroupLeader体育委员类:

public class GroupLeader {
    private List<Girl> girls;
    public GroupLeader(List<Girl> girls) {
        this.girls = girls;
    }
    public void countGirls() {
        System.out.println("女生数量: " + girls.size());
    }
}

在GroupLeader类中定义了一个构造函数,通过构造函数传递了依赖关系。同时,对场景类也进行了一些调整:

public class Client {
    public static void main(String[] args) {
        List<Girl> girls = new ArrayList<>();
        //初始化女生信息
        for (int i = 0; i < 10; i++) {
            girls.add(new Girl());
        }
        ;
        Teacher teacher = new Teacher();
        //老师发布命令
        teacher.command(new GroupLeader(girls));
    }
}

对程序进行了简单的修改,把Teacher的List<Girl>的初始化移动到了场景类中,同时在GroupLeader中增加对Girl的注入,避开了Teacher对陌生类Girl的访问,降低了系统间耦合,提高了系统的健壮性。

(二)、朋友间也是有距离的

人和人之间是有距离的,太远关系逐渐疏远,最终形同陌路;太近就相互刺伤。迪米特法则就是对这个距离进行描述,即使是朋友类之间也不能无话不说,无所不知。

我们在安装软件的时候,经常会有一个导向动作,第一步是确认是否安装,第二步确认Lisence,再然后选择安装目录...这是一个典型的顺序执行动作,具体到程序中就是:调用一个或多个类,先执行第一个方法,然后是第二个方法,根据返回结果再来看是否可以调用第三个方法,或者第四个方法,等等。其类图大体如下:

实现过程如下:

public class Wizard {
    private Random random = new Random(System.currentTimeMillis());
    /**
     * 第一步
     *
     * @return
     */
    public int first() {
        System.out.println("执行第一个方法");
        return random.nextInt(100);
    }
    /**
     * 第二步
     *
     * @return
     */
    public int second() {
        System.out.println("执行第二个方法");
        return random.nextInt(100);
    }
    /**
     * 第三步
     *
     * @return
     */
    public int third() {
        System.out.println("执行第三个方法");
        return random.nextInt(100);
    }
}

在Wizard类中分别定义了三个步骤方法,每个步骤中都有相关的业务逻辑完成指定的任务,我们使用一个随机函数来代替业务执行的返回值。

InstallSoftware类的代码如下:

public class InstallSoftware {
    public void install(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();
                }
            }
        }
    }
}

根据每个方法执行的结果决定是否继续执行下一个方法,模拟人工的选择操作。场景类如下:

public class Client {
    public static void main(String[] args) {
        InstallSoftware installSoftware = new InstallSoftware();
        installSoftware.install(new Wizard());
    }
}

以上程序很简单,运行结果和随机数有关,每次执行的结果都不相同,需要读者自己运行并查看结果。程序虽然简单,但隐藏的问题可不简单,思考一下程序有什么问题?

Wizard类把太多的方法暴露给InstallSoftware类,两者的朋友关系太亲密了,耦合关系变得异常牢固。如果要将Wizard类中的first方法返回值的类型由int修改为boolean,就需要修改InstallSoftware类,从而把修改变更的风险扩散开了。因此,这种耦合是不合适的,我们需要对设计进行重构,重构后的类图如下:

在Wizard类中增加一个installWizard方法,对安装过程进行封装,同时把所有的三个public方法修改为private方法,如下:

public class Wizard {
    private Random random = new Random(System.currentTimeMillis());
    /**
     * 第一步
     *
     * @return
     */
    private int first() {
        System.out.println("执行第一个方法");
        return random.nextInt(100);
    }
    /**
     * 第二步
     *
     * @return
     */
    private int second() {
        System.out.println("执行第二个方法");
        return random.nextInt(100);
    }
    /**
     * 第三步
     *
     * @return
     */
    private int third() {
        System.out.println("执行第三个方法");
        return random.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,同时把InstallSoftware中的方法installWizard()移动到了Wizard类中。通过这样的重构后,Wizard类就只对外公布了一个public方法,即使要修改first方法的返回值,影响的也仅仅是Wizard本身,其他类不受影响,这显示了类的高内聚特性。

修改后的InstallSoftware代码如下:

public class InstallSoftware {
    public void install(Wizard wizard) {
        wizard.installWizard();
    }
}

场景类没有任何改变,通过进行重构,类间的耦合关系变弱了,结构也清晰了,变更引起的风险也变小了。

一个类公开的public属性或者方法越多,修改时涉及的面就越大,变更引起的风险扩散也就越大。因此,为了保持朋友间的距离,在设计时需要反复衡量:是否还可以再减少public属性和方法,是否可以修改为private、package-private、protected等访问权限,是否可以加上final关键字等。

(三)、是自己的就是自己的

在实际应用中经常会出现这样一个方法:放在本类中也可以,放在其他类也没有错,那怎么去衡量呢?可以检查这样一个原则:如果一个方法放在本类中,既不增加类间关系,也对本类不产生负面影响,那就放置在本类中。

三、总结

迪米特法则的核心观念就是类间解耦,弱耦合,只有弱耦合了以后,类的复用率才可以提高。其要求的结果就是产生了大量的中转或跳转类,导致系统的复杂性提高,同时也为维护带来了难度。读者在采用迪米特法则的时候需要反复权衡,既做到让结构清晰,又做到高内聚低耦合。

到此这篇关于Java面向对象设计原则之迪米特法则介绍的文章就介绍到这了,更多相关Java迪米特法则内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java面向对象之继承、构造方法、重写、重载

    目录 构造方法 无参构造 有参构造 注意 类的继承 方法重写.重载 重写 重载 构造方法 类的构造方法是特殊的方法,此方法名称必须和类名一致,构造不能有返回值 不使用void,不能直接调用,在类对象实例化时自动调用,new 的时候可以调用.一般构造方法用于类对象实例化时的初始化.如果一个类没有编写构造方法,系统自动给此类编译时添加一个无参构造方法.如果声明类编写了构造方法,系统不再添加无参构造方法,建议编写完构造方法时,最好编写一个无参构造方法. 无参构造 说白了就是没有参数的构造方法概念看不懂

  • Java面向对象编程的三大特征

    目录 1.封装 1.1.封装概念 1.2.封装的好处 1.3.封装的实现步骤 2.继承 2.1.继承概念 2.2.继承的好处 2.3.子类对象实例化过程 3.多态 3.1.多态基本介绍 3.2.多态的具体体现 方法的多态 3.3.对象的多态(多态的核心) 3.4.对象的多态的应用 多态数组 多态参数 前言: 封装.继承和多态是面向对象编程的三大特征. 1.封装 1.1.封装概念 封装就是把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(

  • Java面向对象之内部类详解

    目录 前言 内部类概述 内部类的分类 成员内部类 局部内部类 前言 在 Java 中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类.本篇博客将总结内部类的使用. 内部类概述 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类. 在 Java 中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类. 内部类一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称.

  • Java基于面向对象实现一个战士小游戏

    目录 一.思路 二.代码实现 设计一个游戏系统,在该系统当中拥有战士 Soldier 角色:,每个角色拥有自己的名字 name .等级 level.生命力 hp ,攻击力 atk.防御力 def:,战士都有普通攻击行为 attack,其实现为根据自己的攻击力减去 被攻击对象的防御力 从而得到对方的失血量:,请设计这个系统中有哪些类,分别有什么属性和行为,有什么关联关系,并在main方法中调用查看效果. 一.思路 首先,我们知道在系统中有个战士的角色叫Soldier,Soldier角色拥有自己的名

  • java 面向对象代码块及不同位置对属性赋值的执行顺序

    目录 代码块 对属性可以赋值的位置 不同位置对属性赋值的执行顺序 结语 前言: java中的代码块介绍以及一个类中不同位置对属性赋值的执行顺序. 代码块 1.代码块的作用:用来初始化类.对象 2.代码块如果有修饰的话,只能使用 static 3.分类:静态代码块 VS 非静态代码块 4.静态代码块 内部可以有输出语句. 随着类的加载而执行,而且只执行一次. 作用:初始化类的信息. 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行. 静态代码块的执行要优先于非静态代码块的执行. 静态代码

  • 详解Java面向对象之多态的原理与实现

    目录 何为多态 代码实现 多态理解 何为多态 定义: 多态是指不同的子类在继承父类后分别都重写覆盖了父类的方法,即父类同一个方法,在继承的子类中表现出不同的形式.系统在运行时(而非编译时),能够根据其类型确定调用哪个重载的成员函数的能力,称为多态性. 特点: (1)多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,就是同一种事物表现出的多种形态. (2)多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话. (3)对不同类的对象

  • Java面向对象设计原则之迪米特法则介绍

    目录 一.迪米特法则的定义 二.迪米特法则的含义 三.总结 一.迪米特法则的定义 迪米特法则,也称为最少知识原则,虽然名字不同,但描述的是同一个规则:一个对象应该对其他对象有最少的了解.通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,被耦合或调用的类的内部是如何复杂都和我没关系,我就知道你提供的这么多public方法,我就调用这么多,其他的我一概不关心. 二.迪米特法则的含义 迪米特法则对类的低耦合提出了明确的规定,其包含以下几层含义. (一).只和朋友交流 迪米特法则,要求只与直接朋友

  • java面向对象设计原则之迪米特法则分析详解

    目录 概念 使用 拓展 概念 迪米特法则解决类与类之间耦合度问题,如果类A调用了B类的某一个方法,则这两个类就形成了一种紧耦合的方式,当B类这个方法发生变化时,一定会影响A类的执行结果.迪米特法则要求每一个类尽可能少的与其他类发生关系,也就是尽可能少的让其他类发生变化时,对其代码的执行结果产生的影响降到最低. 典型情况:A类调用B类的方法,B类和C类是一种关联关系,如果A类通过B类所持有的C类对象直接调用C类的方法,则A类和C类同时拥有强耦合的关系.代码如下: public class B {

  • java面向对象设计原则之开闭原则示例解析

    概念 唯一不变的是不断的变化,在软件开发中应该对需求的变化持开放态度,我们要做的就是如何将这种变化对我们现有的成果带来最小的冲击.开闭原则直接面对面向对象程序的目标扩展性和可维护性,要求对扩展开放,对修改关闭:即在不修改原有代码的情况下改变模块的行为.该原则是面向对象程序设计的总原则,也是度量程序设计的好与坏的唯一标准 实现 开闭原则的实现策略主要在面向对象的封装性和多态性的基础上,利用面向对象的其他原则完成的. 1.使用多态机制解决问题. 如:远程监控系统使用数据传输使用427版本的协议,一年

  • C#实现六大设计原则之迪米特法则

    定义: 一个对象应该对其他对象保持最少的了解. 问题由来: 类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大. 解决方案: 尽量降低类与类之间的耦合. PS: 自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚. 无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率. 低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的. 迪米特法则又叫最少知道原则,最早是在1987年由美国Northe

  • java面向对象设计原则之里氏替换原则示例详解

    目录 概念 实现 拓展 概念 里氏替换原则是任何基类出现的地方,子类一定可以替换它:是建立在基于抽象.多态.继承的基础复用的基石,该原则能够保证系统具有良好的拓展性,同时实现基于多态的抽象机制,能够减少代码冗余. 实现 里氏替换原则要求我们在编码时使用基类或接口去定义对象变量,使用时可以由具体实现对象进行赋值,实现变化的多样性,完成代码对修改的封闭,扩展的开放.如:商城商品结算中,定义结算接口Istrategy,该接口有三个具体实现类,分别为PromotionalStrategy (满减活动,两

  • java面向对象设计原则之单一职责与依赖倒置原则详解

    目录 单一职责概念 实现 拓展 依赖倒置原则概念 示例 拓展 单一职责概念 不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,否则就应该把类拆分.交杂不清的职责将使得代码牵一发而动全身,导致代码混涩难懂,不易修改.难以扩展和复用.如:以前开发C/S程序中的胖客户端程序,就是将人机交互逻辑.业务加工处理逻辑和数据库操作逻辑混合在一起. 实现 单一职责原则是进行类的划分和封装的基本原则,进行类的具体抽象.尽量做到,类的功能单一和清晰化. 1.根据机能划分,使用封装来创建对象之间的分

  • java面向对象设计原则之接口隔离原则示例详解

    目录 概念 实现 拓展 概念 小接口原则,即每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分.如下图所示定义了一个接口,包含了5个方法,实现类A用到了3个方法.实现类B用到了3个方法,类图如下: 类A没有方法4.方法5,却要实现它:类B没有方法2.方法3,但还是要实现这两个方法,不符合接口隔离原则.改造为将其拆分为三个接口,实现方式改为下图所示,符合接口隔离原则: 实现 面向对象机制中一个类可以实现多个接口,通过多重继承分离,通过接口多继承(实现)来实现客户的需求,代码更加清

  • java面向对象设计原则之合成复用原则示例详解

    目录 概念 示例 拓展 概念 尽量使用合成/聚合,而不是使用继承实现复用.所谓的合成/聚合是指一个对象里持有另外一个类的对象,通过调用这些对象的方法得到复用已有功能的目的.如:报文解译程序中,按照继承复用可以设计为: 子类调用父类的方法即可完成水文报文解译.气象解译中通用方法:子类中一定包含了父类的方法,这个叫继承复用. 按照合成/聚合原则设计为: 水文协议和气象协议中,持有编码和位制转换对象,通过调用对象方法即可完成复用. 示例 数据库连接的复用:首先看通过集成关系复用数据连接代码如下 pub

  • Java设计模式七大原则之迪米特法则详解

    目录 定义 案例 需求 方案一 方案二 对比分析 总结 定义 迪米特法则(Law of Demeter, LoD)是1987年秋天由lan holland在美国东北大学一个叫做迪米特的项 目设计提 出的,它要求一个对象应该对其他对象有最少的了解,所以迪米特法则又叫做最少知识原则. 案例 需求 有一个公司,下属有各个部门,现要求打印出公司员工的ID和部门员工的ID 方案一 定义公司员工类 /** * 公司员工 * @author:liyajie * @createTime:2022/2/8 11:

  • Java 设计模式原则之迪米特法则详解

    定义 一个对象应该对其他对象保持最少的了解. 问题由来 类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大. 解决方案 尽量降低类与类之间的耦合. 自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚.无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率.低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的. 迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern Un

随机推荐