前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >六大设计原则之`迪米特法则`

六大设计原则之`迪米特法则`

作者头像
架构探险之道
发布2019-07-25 16:03:53
4440
发布2019-07-25 16:03:53
举报
文章被收录于专栏:架构探险之道

六大设计原则之迪米特法则

定义

迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。英文简写为: LoD.

  • 来源历史 1987年秋天由美国Northeastern University的Ian Holland提出,被UML的创始者之一Booch等普及。后来,因为在经典著作《 The Pragmatic Programmer》而广为人知。
  • 模式与意义 迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。 迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度。 有兴趣可以研究一下设计模式的门面模式(Facade)和中介模式(Mediator),都是迪米特法则应用的例子。 值得一提的是,虽然Ian Holland对计算机科学的贡献也仅限于这一条法则,其他方面的建树不多,但是,这一法则却不仅仅局限于计算机领域,在其他领域也同样适用。比如,美国人就在航天系统的设计中采用这一法则。
  • 描述
    • 狭义的迪米特法则

    如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。 朋友圈的确定 “朋友”条件: 1)当前对象本身(this) 2)以参量形式传入到当前对象方法中的对象 3)当前对象的实例变量直接引用的对象 4)当前对象的实例变量如果是一个聚集,那么聚集中的元素也都是朋友 5)当前对象所创建的对象 任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”;否则就是“陌生人”。 狭义的迪米特法则的缺点: 在系统里造出大量的小方法,这些方法仅仅是传递间接的调用,与系统的商务逻辑无关。 遵循类之间的迪米特法则会是一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联。但是,这也会造成系统的不同模块之间的通信效率降低,也会使系统的不同模块之间不容易协调。 门面模式调停者模式实际上就是迪米特法则的应用。 广义的迪米特法则在类的设计上的体现:

    • 优先考虑将一个类设置成不变类。
    • 尽量降低一个类的访问权限。
    • 谨慎使用Serializable。
    • 尽量降低成员的访问权限。

迪米特法则

迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。

  • 举一个例子:有一个集团公司,下属单位有分公司和直属部门,现在要求打印出所有下属单位的员工ID。先来看一下违反迪米特法则的设计。
代码语言:javascript
复制
//总公司员工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类的直接朋友(以局部变量出现的耦合不属于直接朋友),从逻辑上讲总公司只与他的分公司耦合就行了,与分公司的员工并没有任何联系,这样设计显然是增加了不必要的耦合。按照迪米特法则,应该避免类中出现这样非直接朋友关系的耦合。修改后的代码如下:

代码语言:javascript
复制
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的方法,总公司直接调用来打印,从而避免了与分公司的员工发生耦合。

总结

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

REFRENCES

  1. [设计模式六大原则(5):迪米特法则] (https://blog.csdn.net/zhengzhb/article/details/7296930)
  2. [百度百科 迪米特法则] (https://baike.baidu.com/item/%E8%BF%AA%E7%B1%B3%E7%89%B9%E6%B3%95%E5%88%99)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 架构探险之道 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 迪米特法则
  • 总结
  • REFRENCES
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档