英文名:Law of Demeter,LoD
别名:最少知识原则 Least Knowledge Principle,LKP
迪米特法则法则定义如下:
一个对象应该对其他对象保持最少的了解
类与类关系越密切,耦合度越大
迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的 public 方法,不对外泄露任何信息
迪米特法则还有个更简单的定义:只与直接的朋友通信(Only talk to your immediate friends)
直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
典型的设计模式:中介者模式和门面模式
【例1】打印学生列表
班级类主要由一个ID和班级名称、一个学生列表, 另外还提供一个打印学生列表的Print()方法。 import java.util.ArrayList; import java.util.List; public class MClass { public int id; public String className; public List<Student> students = new ArrayList<>(); public void print(){ for (Student stu : students) { System.out.println("stu id:" + stu.id + " stu name" + stu.studentName); } } } 学生类主要就由ID和学生姓名组成 public class Student { public int id; public String studentName; } School 主要构成同样, 另外也包含了一个Pirnt方法, 他负责打印班级名称和学生名称 import java.util.ArrayList; import java.util.List; public class School { public int id; public String schoolName; public List<MClass> mClasses = new ArrayList<>(); public void print(){ System.out.println("school id:" + id + "school name:" + schoolName); for (MClass mClass : mClasses) { System.out.println("class id:" + mClass.id + "class name:" + mClass.className); List<Student> students = mClass.students; for (Student stu : students) { System.out.println("student id:" + stu.id + "student name:" + stu.studentName); } } } }现在这个设计的主要问题出在School中,根据迪米特法则,只与直接的类发生通信,而Student类并不是School类的直接关系(以局部变量出现的耦合不属于直接关系),从逻辑上讲学校只与他的班级耦合就行了,与班级的学生并没有任何联系,
这样设计显然是增加了不必要的耦合。按照迪米特法则,应该避免类中出现这样非直接关系的耦合。修改后的代码如下:
import java.util.ArrayList; import java.util.List; public class School { public int id; public String schoolName; public List<MClass> mClasses = new ArrayList<>(); public void print(){ System.out.println("school id:" + id + "school name:" + schoolName); for (MClass mClass : mClasses) { System.out.println("class id:" + mClass.id + "class name:" + mClass.className); mClass.print(); } } }修改后,调用班级打印学生名称的方法,学校直接调用来打印,从而避免了与班级的学生发生耦合。
【例2】咖啡机生产咖啡
咖啡机类:
class CoffeeMachine { public void addCoffeeBean() { System.out.println("添加咖啡豆"); } public void addWater() { System.out.println("添加水"); } public void makeCoffee() { System.out.println("制作咖啡"); } }人类:
class Person { public void makeCoffee(CoffeeMachine coffeeMachine) { coffeeMachine.addCoffeeBean(); coffeeMachine.addWater(); coffeeMachine.makeCoffee(); } }人类关心的事情仅仅是让咖啡机制作咖啡,并不关系咖啡是如何制作的,提供较多的开放方法增加了两个类之间的耦合程度,不利于咖啡机类的扩展,假如我们改进了咖啡机的制作工艺,可以自动地加牛奶,这时候,用户需要修改自己地代码。所以我们要减少不必要的联系,实际上也就减少了不必要的麻烦。
修改咖啡机类为仅仅对外暴露一个make方法用于制作咖啡,用户仅仅需要调用这个方法就可以制作咖啡。
咖啡机类:
class CoffeeMachine { private void addCoffeeBean() { System.out.println("添加咖啡豆"); } private void addWater() { System.out.println("添加水"); } private void makeCoffee() { System.out.println("制作咖啡"); } public void make() { addCoffeeBean(); addWater(); makeCoffee(); } }人类:
class Person { public void makeCoffee(CoffeeMachine coffeeMachine) { coffeeMachine.make(); } }迪米特法则的目的,是把我们的类变成一个个“肥宅”。
迪米特法则的核心观念就是类间解耦,弱耦合。迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。只有弱耦合了之后,类的复用才可以提高,类变更的风险才可以减低。但解耦是有限度的,除非是计算机的最小单元–二进制的0和1,否则都是存在耦合的。
但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个“中介”来发生联系,例如本例中,总公司就是通过分公司这个“中介”来与分公司的员工发生联系的。
过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。
迪米特法则的核心是降低类之间的耦合
但是注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系, 并不是要求完全没有依赖关系