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

一、定义迪米特法则

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

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

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

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

3.1 工程结构

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

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

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

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

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

测试结果

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

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

4.1 工程结构

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

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

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

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

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

测试结果

13:49:37.477 [main] INFO  com.lino.design.test.ApiTest - 查询结果{"学生人数":5,"班级平均分":510.2,"班级":"3年1班","老师":"丽华","班级总分数":2551.0}
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Java