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}
相关推荐
MarkHD15 分钟前
javascript 常见设计模式
开发语言·javascript·设计模式
程序员云翼16 分钟前
7-理财平台
java·vue.js·spring boot·后端·毕设
舞者H42 分钟前
源码层面学习动态代理
java·学习
焱行软件科技计算机毕设1 小时前
【java计算机毕设】线上花店销售商城系统java MySQL ssm JSP maven项目代码源码+文档ppt
java·mysql·课程设计
专注成就自我1 小时前
java使用easypoi模版导出word详细步骤
java·开发语言·word
多多*1 小时前
SpringBoot 启动流程六
java·开发语言·spring boot·后端·spring
极乐码农1 小时前
Spring学习03-[Spring容器核心技术IOC学习进阶]
java·学习·spring
m0_588383321 小时前
初学Spring之 JavaConfig 实现配置
java·spring
让你三行代码QAQ1 小时前
SpringSecurity初始化过程
java·开发语言
Enaium2 小时前
Rust入门实战 编写Minecraft启动器#1启动方法
java·后端·rust