Java设计模式:一、六大设计原则-04:迪米特法则

文章目录

一、定义:迪米特法则

  • 迪米特法则(最少知道原则) :Least Knowledge Principle,LKP。
    • 是指一个对象类对于其他对象类来说,知道的越少越好。
    • 也就是说,两个类之间不要有过多的耦合关系,保持最少关联性。
  • 迪米特法则有一句经典语录:只和朋友通信,不和陌生人说话。也就是说,有内在关联的类要内聚,没有直接关系的类要低耦合。

二、模拟场景:迪米特法则原则

  • 模拟学生、老师、校长之间关系的例子来说明迪米特法则。
  • 老师需要负责具体某一个学生的学习情况,而校长会关心老师所在班级的总体成绩,不会过问具体某一个学生的学习情况。
    • 如果校长想知道一个班级的总分和平均分,是应该找老师要,还是跟每一个学生要再进行统计呢?
    • 显然是应该找具体的班主任老师。

三、违背方案:迪米特法则原则

3.1 工程结构

jsx 复制代码
design-1.4-0
|------src
    |------main
        |--java
            |--com.lino.design
                |--Principal.java
                |--Student.java
                |--Teacher.java
    |------test
        |--java
            |--com.lino.design.test
                |--ApiTest.java

3.2 学生、老师、校长类

3.2.1 学生类

Student.java

java 复制代码
package com.lino.design;

/**
 * @description: 学生
 */
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;
    }

    public String getName() {
        return name;
    }

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

    public int getRank() {
        return rank;
    }

    public void setRank(int rank) {
        this.rank = rank;
    }

    public double getGrade() {
        return grade;
    }

    public void setGrade(double grade) {
        this.grade = grade;
    }
}
  • 定义一个学生信息类,包括学生姓名、考试排名、总分。

3.2.2 老师类

Teacher.java

java 复制代码
package com.lino.design;

import java.util.ArrayList;
import java.util.List;

/**
 * @description: 老师
 */
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;
    }
}
  • 定义老师类,在老师类里初始化学生的信息,以及提供基本的信息获取接口。

3.2.3 校长类

Principal.java

java 复制代码
package com.lino.design;

import java.util.HashMap;
import java.util.Map;

/**
 * @description: 校长
 */
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<>(16);
        mapObj.put("班级", teacher.getClazz());
        mapObj.put("老师", teacher.getName());
        mapObj.put("学生人数", stuCount);
        mapObj.put("班级总分数", totalScore);
        mapObj.put("班级平均分", averageScore);
        return mapObj;
    }

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

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

    /**
     * 总人数
     */
    private int clazzStudentCount() {
        return Teacher.getStudentList().size();
    }

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

3.3 单元测试

ApiTest.java

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

测试结果

java 复制代码
13:49:37.477 [main] INFO  com.lino.design.test.ApiTest - 查询结果:{"学生人数":5,"班级平均分":510.2,"班级":"3年1班","老师":"丽华","班级总分数":2551.0}
  • 以上就是通过校长管理所有学生,老师只提供了非常简单的信息。虽然可以查询到结果,但是违背了迪米特法则,因为校长需要了解每个学生的情况。
  • 如果所有班级都让校长类统计,代码就会变得非常臃肿,也不易于维护和扩展。

四、改善代码:迪米特法则原则

4.1 工程结构

java 复制代码
design-1.4-1
|------src
    |------main
        |--java
            |--com.lino.design
                |--Principal.java
                |--Student.java
                |--Teacher.java
    |------test
        |--java
            |--com.lino.design.test
                |--ApiTest.java

4.2 学生、老师、校长类

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

4.2.1 学生类

Student.java

java 复制代码
package com.lino.design;

/**
 * @description: 学生
 */
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;
    }

    public String getName() {
        return name;
    }

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

    public int getRank() {
        return rank;
    }

    public void setRank(int rank) {
        this.rank = rank;
    }

    public double getGrade() {
        return grade;
    }

    public void setGrade(double grade) {
        this.grade = grade;
    }
}
  • 定义一个学生信息类,包括学生姓名、考试排名、总分。

4.2.2 老师类

Teacher.java

java 复制代码
package com.lino.design;

import java.util.ArrayList;
import java.util.List;

/**
 * @description: 老师
 */
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 clazzAverageScore() {
        double totalScore = 0;
        for (Student stu : studentList) {
            totalScore += stu.getGrade();
        }
        return totalScore / studentList.size();
    }

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

    /**
     * 总人数
     */
    public int clazzStudentCount() {
        return studentList.size();
    }

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

    public String getName() {
        return name;
    }

    public String getClazz() {
        return clazz;
    }
}
  • 在使用迪米特法则后,把原来违背迪米特法则的服务接口交给老师类处理。
  • 这样每一位老师都会提供相应的功能,校长类只需要调用使用即可,而不需要了解每一位学生的分数。

4.2.3 校长类

Principal.java

java 复制代码
package com.lino.design;

import java.util.HashMap;
import java.util.Map;

/**
 * @description: 校长
 */
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<>(16);
        mapObj.put("班级", teacher.getClazz());
        mapObj.put("老师", teacher.getName());
        mapObj.put("学生人数", stuCount);
        mapObj.put("班级总分数", totalScore);
        mapObj.put("班级平均分", averageScore);
        return mapObj;
    }
}
  • 校长类直接调用老师类的接口,并获取相应的信息。

4.3 单元测试

ApiTest.java

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

测试结果

java 复制代码
13:49:37.477 [main] INFO  com.lino.design.test.ApiTest - 查询结果:{"学生人数":5,"班级平均分":510.2,"班级":"3年1班","老师":"丽华","班级总分数":2551.0}
相关推荐
金銀銅鐵23 分钟前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(上)
java·后端·ai编程
福建佰胜张工1 小时前
SMC 薄型气爪 MHF2-16D1 深度解析:平行开闭、低剖面结构在自动化装配中的选型与落地
java·服务器·安全·自动化
catoop1 小时前
API Key 设计与安全生成方案:结构拆解、数据库设计与 Java 实现
java·数据库·安全
system_error_x1 小时前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端
驭渊的小故事2 小时前
Spring AI Alibaba 实战:从maven依赖治理到声明式工具调用
java·人工智能·spring
Elastic 中国社区官方博客2 小时前
我们如何通过 70 多个拉取请求,将 OpenTelemetry Java agent 迁移到 invokedynamic
java·大数据·开发语言·elasticsearch·搜索引擎·全文检索·es
tyn1883 小时前
记录本地安装nacos服务的方法及异常
java·开发语言
步行cgn3 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
y1su3 小时前
Leetcode 字符串模板
java·数据结构·算法·leetcode·职场和发展·动态规划
我命由我123455 小时前
Python 开发 - Python 的 pathlib
java·开发语言·后端·python·学习·java-ee·学习方法