敏捷开发_迪米特法则
This browser does not support music or audio playback. Please play it in Weixin or another browser.
一首歌之后刚好看完!
定义
迪米特法则(Law of Demeter,LoD)又叫作最少知识原则(Least Knowledge Principle,LKP),产生于 1987 年美国东北大学(Northeastern University)的一个名为迪米特(Demeter)的研究项目,由伊恩·荷兰(Ian Holland)提出,被 UML 创始者之一的布奇(Booch)普及,后来又因为在经典著作《程序员修炼之道》(The Pragmatic Programmer)提及而广为人知。迪米特法则的定义是:只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。
优点
迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点。
1. 降低了类之间的耦合度,提高了模块的相对独立性。
2. 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。
但是,过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。
实现方法
从迪米特法则的定义和特点可知,它强调以下两点:
1. 从依赖者的角度来说,只依赖应该依赖的对象。
2. 从被依赖者的角度说,只暴露应该暴露的方法。
所以,在运用迪米特法则时要注意以下 6 点。
1. 在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
2. 在类的结构设计上,尽量降低类成员的访问权限。
3. 在类的设计上,优先考虑将一个类设置成不变类。
4. 在对其他类的引用上,将引用其他对象的次数降到最低。
5. 不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。
6. 谨慎使用序列化(Serializable)功能。
实例
有一个集团公司,下属单位有分公司和直属部门,现在要求打印出所有下属单位的员工ID。
初步代码设计如下:
//
总公司员工
class Employee{
private String id;
public void setId(String id){
this.id = id;
}
public String getId(){
return id;
}
}
//
分公司员工
class SubEmployee{
private String id;
public void setId(String id){
this.id = id;
}
public String getId(){
return id;
}
}
class SubCompanyManager{
public List getAllEmployee(){
List list = new ArrayList();
for(int i=0; i<100; i++){
SubEmployee emp = newSubEmployee();
//为分公司人员按顺序分配一个ID
emp.setId("分公司"+i);
list.add(emp);
}
return list;
}
}
class CompanyManager{
public List getAllEmployee(){
List list = new ArrayList();
for(int i=0; i<30; i++){
Employee emp = new Employee();
//为总公司人员按顺序分配一个ID
emp.setId("总公司"+i);
list.add(emp);
}
return list;
}
public void printAllEmployee(SubCompanyManager sub){
List list1 = sub.getAllEmployee();
for(SubEmployee e:list1){
System.out.println(e.getId());
}
List list2 = this.getAllEmployee();
for(Employee e:list2){
System.out.println(e.getId());
}
}
}
public class Client{
public static void main(String[] args){
CompanyManager e = new CompanyManager();
e.printAllEmployee(new SubCompanyManager());
}
}
现在这个设计的主要问题出在CompanyManager中,根据迪米特法则,只与直接的朋友发生通信,而SubEmployee类并不是CompanyManager类的直接朋友(以局部变量出现的耦合不属于直接朋友),从逻辑上讲总公司只与他的分公司耦合就行了,与分公司的员工并没有任何联系,这样设计显然是增加了不必要的耦合。按照迪米特法则,应该避免类中出现这样非直接朋友关系的耦合。
修改一下,去掉非直接关系的耦合。
class SubCompanyManager{
public List getAllEmployee(){
List list = new ArrayList();
for(int i=0; i<100; i++){
SubEmployee emp = newSubEmployee();
//为分公司人员按顺序分配一个ID
emp.setId("分公司"+i);
list.add(emp);
}
return list;
}
public void printEmployee(){
List list = this.getAllEmployee();
for(SubEmployee e:list){
System.out.println(e.getId());
}
}
}
class CompanyManager{
public List getAllEmployee(){
List list = new ArrayList();
for(int i=0; i<30; i++){
Employee emp = new Employee();
//为总公司人员按顺序分配一个ID
emp.setId("总公司"+i);
list.add(emp);
}
return list;
}
public void printAllEmployee(SubCompanyManager sub){
sub.printEmployee();
List list2 = this.getAllEmployee();
for(Employee e:list2){
System.out.println(e.getId());
}
}
}
修改后,为分公司增加了打印人员ID的方法,总公司直接调用来打印,从而避免了与分公司的员工发生耦合。
注意
过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。