前言
说到迪米特原则,后面总能跟上一两句,“最少知道原则,一个类对另一个类知道的越少越好。” 今天主要从代码实例中寻找迪米特的身影,综合实例来看迪米特什么时候适用?迪米特的优缺点?是不是符合迪米特原则的代码就是优质代码?带着这几个问题,我们下面开始探讨。
迪米特
迪米特法则(Law of Demeter),如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:
- 该对象本身
- 被当做方法的参数而传递进来的对象
- 此方法所创建或实例化的任何对象
- 该对象的任何组件
综上所述,将方法的调用保持在界限内,不去调用超出界限范围的方法,就符合了迪米特原则。
如果我们在探讨过程中遇到问题了,就回头看看迪米特法则的定义。
代码实例
我将演示4个例子,读者可以思考这其中哪些例子符合迪米特原则?哪些不符合?说说你的判断标准以及原因?(欢迎在评论区交流讨论,碰撞出激烈的火花)
demo1
Client端
public class client {
public static void main(String[] args) {
Station station = new Station();
Thermometer thermometer = station.getThermometer();
System.out.println(thermometer.getTemperature());
}
}
Station气象站
public class Station {
public Thermometer getThermometer() {
return new Thermometer();
}
}
Thermometer 温度计
public class Thermometer {
public double getTemperature() {
return 10;
}
}
demo2
Client端
public class Client {
public static void main(String[] args) {
Station station = new Station();
double temperature = station.getTemperature();
System.out.println(temperature);
}
}
Station气象站
public class Station {
public double getTemperature() {
Thermometer thermometer = new Thermometer();
return thermometer.getTemperature();
}
}
Thermometer 温度计
public class Thermometer {
public double getTemperature() {
return 10;
}
}
demo3
Client端
public class Client {
public static void main(String[] args) {
Station station = new Station();
System.out.println(station.getTermomoter().getTemperature());
}
}
Station气象站
public class Station {
public Thermometer getThermometer() {
return new Thermometer();
}
}
Thermometer 温度计
public class Thermometer {
public double getTemperature() {
return 10;
}
}
demo4
Client端
public class Client {
public static void main(String[] args) {
System.out.println(new Client().getTemp());
}
public double getTemp() {
Station station = new Station();
Thermometer thermometer = station.getThermometer();
return getTempHelper(thermometer);
}
public double getTempHelper(Thermometer thermometer) {
return thermometer.getTemperature();
}
}
Station气象站
public class Station {
public Thermometer getThermometer(){
return new Thermometer();
}
}
Thermometer 温度计
public class Thermometer {
public double getTemperature() {
return 10;
}
}
揭秘demo面纱
经过分析4个demo,哪些符合迪米特原则呢?且听我慢慢分析,既然是分析,就一定要有理有据,下面我会结合各实例的类图和代码对照进行分析说明。
demo1
查看代码生成的类图我们可以看到 Client 直接和 Station 和 Thermometer 两个类有直接关系,一个类对两个类产生直接关联了就一定不符合迪米特原则吗?且慢,我们要结合代码分析 Client 是否需要和这两个类产生直接关联,我们可以看到 Client 最终的目的只有一个,就是获取温度。目的有一个,但却对两个类产生直接关联,真的有必要吗?我们完全可以通过 Station (气象站)去获取温度,不必直接和 Thermometer(温度计)产生直接的关联。结合定义来看,就是一个类需要调用另一个类的方法的话,可以通过第三者转发这个调用。这里的第三者就是 Station。所以,可以很容易的得出结论,demo1并不符合迪米特原则。
demo2
查看类图发现 Client 只和 Station 产生了直接关联,符合上面我们说的定义,通过第三者(Station)转发这个调用(Client 与 Thermometer 的调用),是典型的符合迪米特原则的例子。
demo3
我们发现,demo3的类图和 demo2类图的类间关系完全一致。那是不是可以由此得出结论,demo3也符合迪米特原则呢?且慢,我们可以看到demo3是链式编程,Client 端中没有显示声明 Thermometer 类的对象,而是隐式的通过 station 调用获取温度计方法返回的 Thermometer 对象调用其中的方法。这种情况如何界定是否符合迪米特原则呢?查看类图发现 Client 和 Thermometer 并没有直接关联,但通过代码可得出,通过 station 对象调用的方法返回的对象进行调用 Thermometer 的方法,所以 Client 还是和 Station 和 Thermometer 有直接关联的,只是没有显示声明而已。结论:demo3 违反了迪米特原则。
demo4
查看类图发现 Client 与 Station类 和 Thermometer类有直接的关系,那是不是可以得出…,真相往往不是这么简单(托眼镜),看到这版例子,是不是想骂上一句,这不是脱裤子**,明明一个方法就可以完成,非要拆成两个方法去调用。先说结论:demo4是符合迪米特原则的,在分析问题卡住的时候,我们不妨回头看看原则的定义是怎么说的,一个类需要调用另一个类的某个方法的话,可以通过第三者转发这个调用。在 demo2中,第三者是 Station,通过 Station 类进行转发了调用。我们分析定义来看,这个第三者指的是什么?在demo2中,第三者是类,而在demo4里,第三者是方法,通过方法转发了调用,一个方法中只调用了一个类的成员方法。我们上面有说到,就任何对象而言,在该对象方法内,符合迪米特原则的调用范围有四点,demo4中也全部符合。通过这种方法转发调用的方式进行解耦。但我们思考一个问题,这样真的有必要吗??
迪米特原则优缺点
迪米特原则的优点,大家都知道,降低类间的耦合关系,毕竟,类间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。
有利一定就有弊,那是不是什么时候都要做到符合迪米特原则?采用迪米特原则会导致更多的“包装”类被制造,以处理和其他组件的沟通,这可能会导致复杂度和开发时间的增加,并降低运行时的性能。
所以,不是任何时刻都要符合迪米特原则,过分的依赖迪米特原则的同时也会带来很多“副作用”。
结论
如何判断代码中是否符合迪米特原则?通过上面分析的经验得出结论:
- 不能仅看类图,需要代码和类图结合进行分析得出结论
- 结合原则定义,最终得出的结论一定不能违背概念