# 迪米特法则
迪米特法则为七大设计原则
之一。
# 定义
迪米特法则(Law Of Demeter, LoD
),也被称为最少知识原则(Least Knowledge Principle, LKP
)。
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
# 反面例子
假设:
- A与B是好朋友,A可以寻找B帮忙
- B与C也是好朋友,B可以寻找C帮忙
- 但A发生了一件事情,只有C可以解决,但A不认识C,怎么办呢?
可以肯定的是,需要寻找B来拜托C帮忙,因为B同时是A和C的好朋友。但是该如何实现呢?
首先来看看错误的实现方式
class A {
constructor(name) {
this.name = name;
}
// A与B是好朋友,所以可以寻找B
getB(name) {
return new B(name);
}
// 一件难题,只有C可以解决
work() {
const b = this.getB('李四');
const c = b.getC('王五');
c.work();
}
}
class B {
constructor(name) {
this.name = name;
}
// B与C是好朋友,所以可以寻找C
getC(name) {
return new C(name);
}
}
class C {
constructor(name) {
this.name = name;
}
// C可以做好这件事
work() {
console.log(this.name + '做好了这件事情');
}
}
// 客户端
const a = new A('张三');
a.work(); // 王五做好了这件事情
可以看到虽然事情是完成了,但是A类里居然有C,但是A是不认识C的。所以这里的设计就不太合理。
实际上这是实际开发场景中常见的一种情况。对象A需要调用对象B的方法,对象B又需要调用对象C的方法。例如A.getXXX().getXXX().getXXX()
类似这样的代码,如果你发现你的代码中也有这样的代码,那就该考虑一下是不是违反了迪米特法则
,是否该重构一下了。
# 正面教材
将以上的代码重构一下,使其符合迪米特法则
。代码如下:
class A {
constructor(name) {
this.name = name;
}
// A与B是好朋友,所以可以寻找B
getB(name) {
return new B(name);
}
// 一件难题,只有C可以解决
work() {
const b = this.getB('李四');
b.work()
}
}
class B {
constructor(name) {
this.name = name;
}
// B与C是好朋友,所以可以寻找C
getC(name) {
return new C(name);
}
work() {
const c = this.getC('王五');
c.work();
}
}
class C {
constructor(name) {
this.name = name;
}
// C可以做好这件事
work() {
console.log(this.name + '做好了这件事情');
}
}
// 客户端
const a = new A('张三');
a.work(); // 王五做好了这件事情
上面代码仅修改了A和B的work方法,使之符合了迪米特法则:
- 类A只与最直接的朋友类B通信,不与类C通信
- 类A只调用类B提供的方法即可,不用关心类B内部是如何实现的(至于B是怎么调用的C,A都不用关心)
相当于只能托自己的朋友办事,但是朋友是怎么帮忙的,我们并不需要知道。
# 总结
迪米特法则的目的是让类之间解耦,降低耦合度。只有这样,类的可复用性才会提高。
但是迪米特的弊端在于会产生大量的中转类或跳转类,导致系统的复杂度提升。