☆ 迪米特法则,又叫最少知识原则,就是说,一个对象应当对其他对象有尽可能少的了解。
ξ 11.1 迪米特法则的各种表述
① 只与你直接的朋友们通信;
② 不要跟“陌生人”说话;
③ 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
ξ 11.2 狭义的迪米特法则
☆ 如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另外一个类的某一个方法,可以通过第三者转发这个调用。
参考下例,Someone、Friend和Stranger三个类。
Someone类有一个方法接受一个Friend类型的变量:
... {
public void operation1( Friend friend )
...{
Stranger stranger = friend.provide() ;
stranger.operation3() ;
}
}
所以Someone和Friend是朋友类(直接通讯的类)。
同理,Friend类持有一个Stranger类的私有对象,他们是朋友类:
... {
private Stranger stranger = new Stranger() ;
public void operation2()...{}
public Stranger provide()
...{
return stranger ;
}
}
在这里,Someone类和Stranger类不是朋友类,但Someone类却通过Friend类知道了Stranger类的存在,这显然违反迪米特法则。
现在,我们对Someone和Friend类进行重构。首先在Friend类里添加一个方法,封装对Stranger类的操作:
... {
private Stranger stranger = new Stranger() ;
public void operation2()...{}
public Stranger provide()
...{
return stranger ;
}
public void forward()
...{
stranger.operation3() ;
}
}
然后,我们重构Someone的operation1方法,让其调用新提供的forward方法:
... {
public void operation1( Friend friend )
...{
friend.forward() ;
}
}
现在Someone对Stranger的依赖完全通过Friend隔离,这样的结构已经符合狭义迪米特法则了。
仔细观察上述结构,会发现狭义迪米特法则一个明显的缺点:会在系统里造出大量的小方法,散落在系统的各个角落。这些方法仅仅是传递间接的调用,因此与系统的商务逻辑无关,当设计师试图从一张类图看出总体的框架时,这些小的方法会造成迷惑和困扰。遵循迪米特法则会使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接关联。但是,这也会造成系统的不同模块之间的通信效率降低,也会使系统的不同模块之间不容易协调。
结合依赖倒转原则,我们对代码进行如下重构来解决这个问题,首先添加一个抽象的Stranger类,使Someone依赖于抽象的“Stranger”角色,而不是具体实现:
... {
abstract void operation3() ;
}
然后,让Stranger从该类继承:
... {
public void operation3() ...{}
}
随后,我们重构Someone使其依赖抽象的Stranger角色:
... {
public void operation1( Friend friend )
...{
AbstractStranger stranger = friend.provide() ;
stranger.operation3() ;
}
}
最后,我们重构Friend的provide方法,使其返回抽象角色:
... {
private Stranger stranger = new Stranger() ;
public void operation2()...{}
public AbstractStranger provide()
...{
return stranger ;
}
}
现在,AbstractStranger成为Someone的朋友类,而Friend类可以随时替换掉AbstractStranger的实现类,Someone不再需要了解Stranger的内部实现细节。下图是重构后的UML类图:
ξ 11.3 迪米特法则与设计模式
对迪米特法则的最好描述,可以参考门面模式和调停者模式。
ξ 11.4 广义迪米特法则
☆ 在将迪米特法则运用到系统的设计中时,应注意的几点:
① 在类的划分上,应该创建有弱耦合的类;
② 在类的结构设计上,每一个类都应当尽量降低成员的访问权限;
③ 在类的设计上,只要有可能,一个类应当设计成不变类;
④ 在对其他类的引用上,一个对象对其它对象的引用应当降到最低;
⑤ 尽量降低类的访问权限;
⑥ 谨慎使用序列化功能;
⑦ 不要暴露类成员,而应该提供相应的访问器(属性)。