也称为最少知道原则(Least Knowledge Principle 简写LKP)
迪米特法则是用来干什么的?
他的初衷是:降低类之间的耦合。
以我目前的视野,我认为迪米特法则就是一个在类创建方法和属性时需要遵守的法则。
什么是迪米特法则?
迪米特法则由三个守则组成:
守则1:是自己的就是自己的
如果一个方法放在本类中,即不增加类间关系,也对本类不产生负面影响就放在本类中。
守则2:一个类只与他的朋友类进行交互。talk only to your immediate friends
在弄明白这个守则之前,我们需要弄清什么叫做朋友类:
在遵守守则1的前提下,为类A创建了一个方法,那么该方法Method中的参数必然是某基本类型(包括void类型)的变量,或者是某个类的对象。
如果是后者即某个类的对象,我们将该类称之为类B,那么类B即为类A的朋友类。
那么守则2的意思是类A的方法Method只与类B以及类A的属性进行交流,在该方法中不再引入其他第三方类。
除了方法中的参数的类型可能成为朋友类,属性的类型也是朋友类(基本类型除外)。
以下给出例子:
例如,老是想让体育委员确认以下女生人数,我们来看怎么用程序来实现,类图如图:
Teacher类的commond方法负责发送命令给体育委员,命令他清点女生,其实现过程如下:
老师只有一个commond方法,先定义出所有的女生,然后发命令给体育委员,去清点以下女生的数量。体育委员GroupLeader的实现过程如下:
老师类和体育委员类都对女生类产生了依赖,而且女生类不需要执行任何动作,因此定义一个空类,如图:
故事中的三个角色都有了,在定义一个场景来描述这个故事,实现过程如下:
运行结果:
体育委员按照老师的要求对女生进行了清点,并得出了数量。我们回过头来思考一下这个程序有什么问题,首先确定Teacher有几个朋友类,他仅有一个朋友类:GroupLeader。为什么Girl不是朋友类呢?Teacher也对他产生了依赖关系啊!朋友类的定义是这样的:出现在成员变量,方法的输入输出中的类称为成员朋友类。迪米特法则告诉我们一个类只和朋友类交流,但是我们刚刚定义的commond方法却与Girl类有了交流,声明了一个List动态数组,也就是与一个陌生的类Girl有了交流,这样就破坏了Teacher的健壮性。方法是类的一个行为,类竟然不知道与其他类产生了依赖关系,这是不允许的,严重违反了迪米特法则。
可以这样子修改,在类图中去掉Teacher对Girl类的依赖关系,修改后的Teacher类如下:
修改后的体育委员类:
修改后的场景类:
把Teacher中对List的初始化移动到了场景类中,同时在GroupLeader中增加了对Girl的注入,避开了Teacher类对陌生类Girl的访问,降低了系统间的耦合,提高了系统的健壮性。
注意:一个类只和朋友交流,不与陌生类交流,类与类之间的关系是建立在类间的,而不是方法间,因此一个方法尽量不引入一个类中不存在的对象,当然JDK API提供的类除外。
守则3:朋友间也是有距离的
朋友间是有距离的,太远关系逐渐疏远,最终形成陌路;太近就互相刺伤,必须保持一个合适的距离。迪米特法则就是对这个距离的描述,即使是朋友类之间也不能无话不说,无所不知。
例如,我们在安装软时,经常有一个导向动作,第一步是确认是否安装,第二步是确认License,然后再选择安装目录;这是一个典型的顺序执行动作,具体到程序中就是:调用一个或者多个类,先执行第一个方法,然后是第二个方法,根据返回结果再来看是否调用第三个方法,其类图如下:
很简单的类图,实现软件安装的过程,其中first方法定义第一步做什么,second方法定义第二步做什么,其实现过程如下:
程序虽然简单,隐藏的问题可不简单,思考一下程序有什么问题。Wizard类把太多的方法暴露给InstallSoftware类,两者的朋友关系太紧密了,耦合关系变得异常牢固。如果要将Wizard类中的first方法返回值的类型由int改为boolean,就需要修改InstallSoftware类,从而把修改变更的风险扩散开来。因此这样的耦合是极度不合适的,我们需要重新对设计进行重构,重构后的类图如下:
在Wizard类方法中增加一个installWizard方法,对安装过程进行封装,同时把原有的三个public方法修改为private方法,如下所示:
修改后的InstallSoftware类:
通过这样的重构后,Wizard类对外只公布了一个public方法,即使要修改first的返回值,影响的也只是Wizard本身,其他类不受影响,这显示了类的高内聚特性。
注意:迪米特法则要求类“羞涩”一点,尽量不要对外公布太多的public方法和非静态的public变量,尽量内敛,多使用private、protected等访问权限。