设计模式 - 六大设计原则之LoD(迪米特法则原则)

news2024/11/26 16:37:35

文章目录

  • 概述
  • Case
  • 学生类
  • 老师类
  • Bad Impl
  • Better Impl
    • 老师类改造
    • 调用方:校长类改造

在这里插入图片描述


概述

迪米特法(Law Of Demeter , LoD)则又叫最少知道原则(Least Knowledge Principle),最早是在1987年由美国Northeastern University的Ian Holland提出。

通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。

迪米特法有一个经典语录:只与朋友通信,不和陌生人说话。

首先来解释一下什么是朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。

耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。


Case

学生、老师、校长 来说明迪米特法则。

老师需要负责具体某一个学生的学习情况, 校长只关心老师所在班级的总体成绩,不会过问具体某一个学生的学习情况。

试想一下,校长想知道一个班级的总分和平均分,是应该找老师要 还是 跟每一个学生要然后再进行统计呢? 显然是应该跟老师要。 实际开发中,很容易犯这种错误。


学生类

public class Student {

    private String name;    // 学生姓名
    private int rank;       // 考试排名(总排名)
    private double grade;   // 考试分数(总分)

    public Student() {
    }

    public Student(String name, int rank, double grade) {
        this.name = name;
        this.rank = rank;
        this.grade = grade;
    }

   // set get 
}

在这里插入图片描述

老师类

在教师类中初始化学生的信息,提供基本的信息获取接口。

public class Teacher {

    private String name;                // 老师名称
    private String clazz;               // 班级
    private static List<Student> studentList;  // 学生

    public Teacher() {
    }

    public Teacher(String name, String clazz) {
        this.name = name;
        this.clazz = clazz;
    }

    static {
        studentList = new ArrayList<>();
        studentList.add(new Student("花花", 10, 589));
        studentList.add(new Student("豆豆", 54, 356));
        studentList.add(new Student("秋雅", 23, 439));
        studentList.add(new Student("皮皮", 2, 665));
        studentList.add(new Student("蛋蛋", 19, 502));
    }

    public static List<Student> getStudentList() {
        return studentList;
    }

    public String getName() {
        return name;
    }

    public String getClazz() {
        return clazz;
    }
}


在这里插入图片描述


Bad Impl

【校长类】

校长管理全局,并在校长类中获取学生人数、总分、平均分等

public class Principal {

    private Teacher teacher = new Teacher("丽华", "3年1班");

    // 查询班级信息,总分数、学生人数、平均值
    public Map<String, Object> queryClazzInfo(String clazzId) {
        // 获取班级信息;学生总人数、总分、平均分
        int stuCount = clazzStudentCount();
        double totalScore = clazzTotalScore();
        double averageScore = clazzAverageScore();

        // 组装对象,实际业务开发会有对应的类
        Map<String, Object> mapObj = new HashMap<>();
        mapObj.put("班级", teacher.getClazz());
        mapObj.put("老师", teacher.getName());
        mapObj.put("学生人数", stuCount);
        mapObj.put("班级总分数", totalScore);
        mapObj.put("班级平均分", averageScore);
        return mapObj;
    }

    // 总分
    public double clazzTotalScore() {
        double totalScore = 0;
        for (Student stu : teacher.getStudentList()) {
            totalScore += stu.getGrade();
        }
        return totalScore;
    }

    // 平均分
    public double clazzAverageScore(){
        double totalScore = 0;
        for (Student stu : teacher.getStudentList()) {
            totalScore += stu.getGrade();
        }
        return totalScore / teacher.getStudentList().size();
    }

    // 班级人数
    public int clazzStudentCount(){
        return teacher.getStudentList().size();
    }

}

在这里插入图片描述

以上是通过校长管理所有的学生信息, 老师只是提供了非常简单的信息。虽然可以查询到结果,但违背了迪米特法则,因为校长需要了解每个学生的情况。 如果所有的班级都让校长类去统计,代码将会变得非常的臃肿,也不易于扩展和维护。


看下单测

    @Test
    public void test_Principal() {
        Principal principal = new Principal();
        Map<String, Object> map = principal.queryClazzInfo("3年1班");
        logger.info("查询结果:{}", JSON.toJSONString(map));
    }

Better Impl

上述的实现我们可以发现,不应该让校长直接管理学生,校长应该管理老师,由老师提供相应的学生信息查询服务,那么接下来我们把校长要的信息交给老师类去处理。

老师类改造

public class Teacher {

    private String name;                // 老师名称
    private String clazz;               // 班级
    private static List<Student> studentList;  // 学生

    public Teacher() {
    }

    public Teacher(String name, String clazz) {
        this.name = name;
        this.clazz = clazz;
    }

    static {
        studentList = new ArrayList<>();
        studentList.add(new Student("花花", 10, 589));
        studentList.add(new Student("豆豆", 54, 356));
        studentList.add(new Student("秋雅", 23, 439));
        studentList.add(new Student("皮皮", 2, 665));
        studentList.add(new Student("蛋蛋", 19, 502));
    }

    // 总分
    public double clazzTotalScore() {
        double totalScore = 0;
        for (Student stu : studentList) {
            totalScore += stu.getGrade();
        }
        return totalScore;
    }

    // 平均分
    public double clazzAverageScore(){
        double totalScore = 0;
        for (Student stu : studentList) {
            totalScore += stu.getGrade();
        }
        return totalScore / studentList.size();
    }

    // 班级人数
    public int clazzStudentCount(){
        return studentList.size();
    }

    public String getName() {
        return name;
    }

    public String getClazz() {
        return clazz;
    }
}

在这里插入图片描述

使用迪米特法则 后,把燕来违背迪米特法则的服务接口交给老师类处理,这样每一位老师都会提供相应的功能,校长只需要调用即可,不需要了解每一位学生的分数。


调用方:校长类改造

那看看校长类如何使用的

public class Principal {

    private Teacher teacher = new Teacher("丽华", "3年1班");

    // 查询班级信息,总分数、学生人数、平均值
    public Map<String, Object> queryClazzInfo(String clazzId) {
        // 获取班级信息;学生总人数、总分、平均分
        int stuCount = teacher.clazzStudentCount();
        double totalScore = teacher.clazzTotalScore();
        double averageScore = teacher.clazzAverageScore();

        // 组装对象,实际业务开发会有对应的类
        Map<String, Object> mapObj = new HashMap<>();
        mapObj.put("班级", teacher.getClazz());
        mapObj.put("老师", teacher.getName());
        mapObj.put("学生人数", stuCount);
        mapObj.put("班级总分数", totalScore);
        mapObj.put("班级平均分", averageScore);
        return mapObj;
    }

}

在这里插入图片描述

校长直接调用老师的接口,并回获取相应的结果信息。 这样一来,整个功能逻辑就非常清晰了。

单测如下

    @Test
    public void test_Principal() {
        Principal principal = new Principal();
        Map<String, Object> map = principal.queryClazzInfo("3年1班");
        logger.info("查询结果:{}", JSON.toJSONString(map));
    }

在这里插入图片描述


在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【游戏客户端】如何实现环形进度条

【游戏客户端】如何实现环形进度条 Hello大家好&#xff0c;我是Lampard。好久没写博客了&#xff0c;之前在忙着制作项目的一个大的副本&#xff0c;趁着过年得闲&#xff0c;和大家分享一下制作过程中遇到的一些有趣的问题。今天主要是分享如何在cocos制作一个环形的进度条 (…

重写 equals 时为什么一定要重写 hashCode

equals 方法和 hashCode 方法是 Object 类中的两个基础方法&#xff0c;它们共同协作来判断两个对象是否相等。为什么要这样设计嘞&#xff1f;原因就出在“性能” 2 字上。 使用过 HashMap 我们就知道&#xff0c;通过 hash 计算之后&#xff0c;我们就可以直接定位出某个值存…

移动web 空间转换 3D

移动web 空间转换 3D空间转换 3D3D位移透视3D旋rotateXrotateY左手法则立体呈现空间转换 3D 3D坐标系 3D 坐标系比2D 多了一个Z轴。 一定要记住3个坐标轴取值的正反&#xff1a; X 轴 往右越大&#xff0c;是正值&#xff0c; 否则反之Y 轴 往下越大&#xff0c;是正值&…

React错误边界

首先 我们先构建出问题的场景 我们创建一个react项目 然后在src下创建 components 文件夹目录 在下面创建一个 error.jsx 组件 参开代码如下 import React from "react";export default class App extends React.Component{constructor(props){super(props);this.…

CUDA编程笔记(5)

文章目录前言CUDA的内存组织全局内存常量内存纹理内存和表面内存寄存器局部内存共享内存L1和L2缓存SM的构成API函数查询设备总结前言 cuda的内存组织&#xff0c;在使用GPU时尽可能提高性能&#xff0c;合理的使用设备的内存也是十分重要的。 CUDA的内存组织 如表所示&#…

Docker基本操作

Docker基本操作一、镜像操作1.镜像名称2.镜像命令&#xff08;1&#xff09;拉取、查看镜像&#xff08;2&#xff09;保存、导入镜像二、容器操作1.容器相关命令2.创建并运行一个容器3.进入容器&#xff0c;修改文件4.小结三、数据卷&#xff08;容器数据管理&#xff09;1.什…

Java:枚举类型

Java&#xff1a;枚举类型 每博一文案 师父说&#xff1a;人活一世&#xff0c;每个人都有他的特别&#xff0c;每个人都值得被温柔相待。红尘一遭&#xff0c;每段经历都有它的必然&#xff0c; 每段经历都造就了现在的你&#xff0c;最快乐的事情&#xff0c;就是做自己&…

Java多线程案例之定时器

一. 定时器概述 1. 什么是定时器 定时器是一种实际开发中非常常用的组件, 类似于一个 “闹钟”, 达到一个设定的时间之后, 就执行某个指定好的代码. 比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连.比如一个 Map, 希望里面的某个 key 在 3s 之后过期(自…

排序算法: 数据的离散化(排序+去重 C++例题实现)

文章目录数据的离散化例题&#xff1a;电影完整代码数据的离散化 离散化是指将一个无穷大的集合中的若干个元素映射到一个有限的集合中&#xff0c;以便于对那个无穷大的集合进行操作。 在很多情况下&#xff1a;对于一个规定在Z范围内的整数范围&#xff0c;他有可能包含非常…

maven创建自定义web工程模板

一&#xff0c;先搭建好一个项目模板。 注意每个文件夹下都放一个文件占位&#xff0c;否则创建模板时会认为是空目录不进行创建。 注意项目文件夹名字 和 pom.xml 中<artifactId 和 <name 的名字都使用相同的名字&#xff0c;写一个好记的名字&#xff0c;因为后面生…

QT UI布局设置整理-边框设置

一、设置边距的方法 1、设置容器内部的内容控件的边距 //设置容器leftBar&#xff08;QWidget&#xff09;内部marginui->leftBar->setContentsMargins(10,10,0,0); 2、 设置内部控件之间的间距 //editWidget是一个QWidget ui->editWidget->layout()->setSpac…

【云原生kubernetes】k8s中控制器使用详解

一、什么是控制器 控制器是管理pod的中间层&#xff0c;只需要告诉Pod控制器&#xff0c;想要创建多少个什么样的Pod&#xff0c;它会创建出满足条件的Pod &#xff1b;控制器相当于一个状态机&#xff0c;用来控制Pod的具体状态和行为 &#xff1b;controller会自动创建相应的…

【数据库概论】3.2 SQL的查询、更新和删除语句

一、 数据查询 SQL提供SELECT语句用于查询&#xff0c;一般格式为&#xff1a; 根据WHERE子句条件表达式从FROM子句指定的基本表、视图中找出满足条件的元组 GROUP BY语句则用作将结果按照<列名1>的值进行分组&#xff0c;该属性列值相等的元组为一个组&#xff1b;ORD…

Essential C++第五章习题

目录 5.1 5.2 5.3 5.4 5.1 C代码&#xff1a; //Stack.h#include<vector> #include<string> #include<iostream> using namespace std;#pragma once#ifndef _STACK_H_ #define _STACK_H_typedef string elemType;class Stack { public://基类的析构函数…

【JavaSE专栏5】Java 基本数据类型和取值范围

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…

Mine Goose Duck 0.2版本发布

本次我增加了模组的1.16.5和1.18.2的适用版本&#xff0c;新增了一些职业和装扮 1.新职业 1.冒险家 你不会死于摔伤、溺水、火烧、冰冻。 2.工程师 你可以修改888范围内红石设备的状态。 3.模仿者 怪物认为你是他们的一员。 4.加拿大鹅 你会自动报警并召唤警车。 5.…

深度卷积对抗神经网络 基础 第三部分 (WGAN-GP)

深度卷积对抗神经网络 基础 第三部分 (WGAN-GP&#xff09; Wasserstein GAN with Gradient Penalty (WGAN-GP) 我们在训练对抗神经网络的时候总是出现各种各样的问题。比如说模式奔溃 (mode collapse)和 梯度消失&#xff08;vanishing gradient&#xff09;的问题。 比如说…

在linux下安装docker

文章目录 目录 文章目录 前言 一、docker 二、使用步骤 1.环境准备 2.安装 三、配置阿里云镜像加速 四、卸载 总结 前言 一、docker 镜像&#xff08;image&#xff09;&#xff1a; docker镜像就好比是一个模板&#xff0c;可以通过这个模板来创建容器服务&#xff0c;tomc…

【攻坚克难】详解k8s持久化存储数据pv、pvc存储问题

问题 如图:pod中的容器,创建一个包含文件的目录,重启pod或系统重启后,此目录及其文件都会丢失,如何保证其不会丢失? 图 1 创建包含文件的目录 方法 分析:用pv、pvc为k8s持久化存储数据是最好的选择,可解决上述问题。流程:pv → pvc → pod把创建的目录挂载到pvc上步…

路由 OSPF 优化(FA地址、路由汇总、路由过滤、区域认证、接口认证)

1.2.0 路由 OSPF 优化&#xff08;FA地址、路由汇总、路由过滤、区域认证、接口认证&#xff09; 一、FA地址 该文章介绍的FA地址说辞简单易懂&#xff1a;路由协议系列之六&#xff1a;OSPF FA地址 产生条件 ASBR在其连接外部网络的接口&#xff08;外部路由的出接口&#xf…