什么是迪米特法则
迪米特法则(Law of Demeter, LoD),又被称为最少知识原则,这个原则规定了,一个对象应该对另一个对象有最少的了解,什么又是最少的了解,就是减少一个对象在另一个对象出现的次数,甚至不出现,减少类于类之间的耦合关系。
一个类对另一个类的了解,都是基于其public方法。
迪米特法则还有一个英文解释:Only talk to your immediate friends,只于一个类之间的朋友进行交流。
什么是类于类之间的交流?无非就是一个类中的方法中调用另一个类的方法。
什么是直接朋友?一个类的朋友就是这个类本身、这个类的成员对象、这个对象创建的对象、当前对象的方法参数等等。
参考如下代码,Address,Book,Stuent这些都是Teacher的朋友。
public class Teacher {
private Address address;
public void tech(Book book){
System.out.println(book.getName);
}
public Studnet findStudent(){
return Student();
}
}
只和朋友交流
如何做到之和朋友类交流,来看一个案例:
老师想让班长去清点一下班级的人数,让我们用代码去实现这个场景;
先看类图:
代码:
public class Student {
}
public class Teacher {
void command(Monitor monitor){
List<Student> listStudent = new ArrayList<>();
for (int i=0; i<3; i++){
listStudent.add(new Student());
}
monitor.countStudent(listStudent);
}
}
public class Monitor {
void countStudent(List<Student> list){
System.out.println("学生人数:" + list.size());
}
}
public class Client {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.command(new Monitor());
}
}
运行结果:
运行结果很成功,学生人数被统计出来了。
但是这样的设计有什么问题?
首先Teacher类有几个朋友类,只有Monitor类(成员变量),那这里Student类是它的朋友类吗?不是,Student类是在方法中产生的,不是朋友类,但它却出现在了Teacher类中,这就相当于和Teacher类产生了交流,产生了依赖,这样回导致,如果以后Student类发生了改变,Teacher类也得同步参数修改,这是严重不允许的,严重违反迪米特法则。
让我们去修改一下这个类图结构:
public class Student {
}
public class Monitor {
private List<Student> list;
public Monitor(List<Student> list) {
this.list = list;
}
void countStudent(){
System.out.println("学生人数:" + list.size());
}
}
public class Teacher {
void command(Monitor monitor){
monitor.countStudent();
}
}
public class Client {
public static void main(String[] args) {
Teacher teacher = new Teacher();
List<Student> listStudent = new ArrayList<>();
for (int i=0; i<3; i++){
listStudent.add(new Student());
}
teacher.command(new Monitor(listStudent));
}
}
观察改进前和改进后的代码有什么区别,改进前,Monitor类需要统计Student类,需要一个List,通过它的上级调用类,也就是Teacher类创建得到,虽然完成了业务,但是却导致Teacher类和它的非朋友类Student产生了交流,以后如果Student类有改动,Teacher类也会跟着改,于是进行修改,把Monitor类和Studnet类的依赖用成员变量的方式进行依赖,通过构造方法进行传递,这样Teacher类就可以不用和Studnet类进行交流,就能完成业务了。
朋友之间交流也不要太密切
迪米特法则中规定,一个类中,只与这个类的朋友进行联系,说简单点,就是一个类的方法中,如果一个类不是这个类的成员变量、函数参数、或者这个类通过方法创建出来的,最好不要出现在这个方法中。
但是,即使这个某个类是这个类的朋友,也应该尽可能的减少交流,也就是调用。
比如说,我们的电脑要安装一个软件,是不是要点击软件包,然后一直点下一步,如果某一步失败,后续的步骤也不能继续往下执行了。
来用代码表示是什么样的?
public class Install {
int checkDir(){
System.out.println("检查目录");
return 1;
}
int checkSpace(){
System.out.println("检查空间");
return 1;
}
int install(){
System.out.println("安装软件");
return 1;
}
}
public class Computer {
private Install install;
public Computer(Install install) {
this.install = install;
}
void installSoftWare(){
//检查目录是否存在
int i = install.checkDir();
if (i==1){
//检查空间是否足够
int i1 = install.checkSpace();
if (i1==1){
int install = this.install.install();
if (install==1){
System.out.println("安装成功");
return;
}else {
System.out.println("安装失败");
return;
}
}
}
System.out.println("安装失败");
}
}
仔细看Computer类安装的方法,它和之间的朋友类Install进行交流,每次步骤返回1,代表成功,就进行下一步。这样的代码也许能模拟这个业务,但是它有什么问题?我们得考虑以后代码变动的风险,如果以后,每个安装步骤的返回值,不在返回1代表成功,而是布尔类型的true代表成功,这样回导致Computer类所有对这些方法调用的地方都得修改。
为了改进这种情况,我们需要对类结构进行修改,如下:
public class Install {
private int checkDir(){
System.out.println("检查目录");
return 1;
}
private int checkSpace(){
System.out.println("检查空间");
return 1;
}
private int install(){
System.out.println("安装软件");
return 1;
}
public void installSoftForComputer(){
int i = checkDir();
if (i==1){
//检查空间是否足够
int i1 = checkSpace();
if (i1==1){
int install = install();
if (install==1){
System.out.println("安装成功");
return;
}else {
System.out.println("安装失败");
return;
}
}
}
System.out.println("安装失败");
}
}
public class Computer {
private Install install;
public Computer(Install install) {
this.install = install;
}
void installSoftWare(){
//检查目录是否存在
install.installSoftForComputer();
}
}
看到改变前后的区别了吗?我把之前computer需要一个一个去调用的方法,封装在了Install一个public方法中,并且把每个安装步骤都设置为私有,这样有什么好处?我们减少了类与类之间的交流,同时也减少了它们变更带了的风险,以后Install类的变更,比如变更返回类型,那也只对Install内部有影响,这就是我们常说的高内聚!