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

Posted 小小工匠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式 - 六大设计原则之LoD(迪米特法则原则)相关的知识,希望对你有一定的参考价值。

文章目录


概述

迪米特法(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));
    


以上是关于设计模式 - 六大设计原则之LoD(迪米特法则原则)的主要内容,如果未能解决你的问题,请参考以下文章

设计模式六大原则/接口设计六大原则 之 迪米特法则(转)

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

设计模式六大原则:迪米特法则

设计模式六大原则:迪米特法则

学习设计模式 - 六大基本原则之迪米特法则

六大设计原则之迪米特法则