设计模式系列:迪米特法则

来源:互联网 发布:微信小程序读写数据库 编辑:程序博客网 时间:2024/05/16 15:33

问题由来

问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

解决方案:尽量降低类与类之间的耦合。
自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。

定义

一个软件实体应当尽可能少的与其他实体发生相互作用。一个对象应该对其他对象有最少的了解。通俗的讲,一个类应该对自己需要耦合调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂都和我没关系,那是你的事情,我就知道你提供的这么多public方法,我就调用这么多,其他的我一概不关心。即:一个对象对其他对象的引用应该降到最低。

迪米特法则对类的低耦合做了明确的要求,其包含以下4层含义:

  • 只和朋友交流
    朋友类的定义是这样的,出现在成员变量、方法的输入输出参数中的类成为成员朋友类,如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友。而出现在方法内部的类不属于朋友类。

  • 朋友间也是有距离的
    一个类公开的public属性或方法越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大。因此,为了保持朋友类间的距离,在设计时需要反复衡量:是否可以再减少public方法和属性,是否可以修改为private、protected等访问权限,是否可以加上final关键字等。

  • 是自己的就是自己的
    在实际应用中经常会出现这样的一个方法:放在本类中也可以,放在其他类中也没有错,那怎么去衡量呢?你可以坚持这样一个原则:如果一个方法放在本类中,既不增加类间关系,也不对本类产生负面影响,就放置在本类中。

  • 谨慎使用serializable
    RMI中注意

最佳实践

迪米特法则的核心观念就是:类间解耦、弱耦合,只有弱耦合了,类的复用性才可以提高,其要求的结果就是产生了大量的中转或跳转类,导致系统的复杂性提高,同时也为维护带来了难度。我们在使用时要反复权衡,既做到结构清晰,又做到高内聚低耦合。另外解耦是有限度的,需要适当的考虑这个原则。防止过犹不及。

例子

举一个例子:有一个集团公司,下属单位有分公司和直属部门,现在要求打印出所有下属单位的员工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<SubEmployee> getAllEmployee(){              List<SubEmployee> list = new ArrayList<SubEmployee>();              for(int i=0; i<100; i++){                  SubEmployee emp = new SubEmployee();                  //为分公司人员按顺序分配一个ID                  emp.setId("分公司"+i);                  list.add(emp);              }              return list;          }      }      class CompanyManager{          public List<Employee> getAllEmployee(){              List<Employee> list = new ArrayList<Employee>();              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<SubEmployee> list1 = sub.getAllEmployee();              for(SubEmployee e:list1){                  System.out.println(e.getId());              }              List<Employee> 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<SubEmployee> getAllEmployee(){              List<SubEmployee> list = new ArrayList<SubEmployee>();              for(int i=0; i<100; i++){                  SubEmployee emp = new SubEmployee();                  //为分公司人员按顺序分配一个ID              emp.setId("分公司"+i);                  list.add(emp);              }              return list;          }          public void printEmployee(){              List<SubEmployee> list = this.getAllEmployee();              for(SubEmployee e:list){                  System.out.println(e.getId());              }          }      }      class CompanyManager{          public List<Employee> getAllEmployee(){              List<Employee> list = new ArrayList<Employee>();              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<Employee> list2 = this.getAllEmployee();              for(Employee e:list2){                  System.out.println(e.getId());              }          }      }  

修改后,为分公司增加了打印人员ID的方法,总公司直接调用来打印,从而避免了与分公司的员工发生耦合。
迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个“中介”来发生联系,例如本例中,总公司就是通过分公司这个“中介”来与分公司的员工发生联系的。过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合

0 0