迪米特法则(LoD)

迪米特原则的定义

迪米特原则(Law of Demeter,LoD),也叫最少知识原则(Low knowledge Principle,LKP):

一个对象应该对其他对象有最少的了解。

通俗的讲:一个类对自己需要耦合或调用的类知道的最少,你(被耦合或调用的类)的内部是如何复杂和我没有关系,我就知道你提供的public方法,我只调用这些方法,其它的我不关心。

迪米特原则的具体要求

迪米特原则对类的低耦合提出了明确的要求:

只与朋友类交流

迪米特原则还有一个解释:Only talk to your immediate friends(只与直接朋友通信)。
什么叫直接朋友呢?每个对象都必然会与其他对象有耦合关系,两个对象之间的耦合就成为朋友关系,这种关系类型有很多,例如:组合,聚合,依赖等。朋友类也可以这样定义:出现在成员变量,方法的输入输出参数中的类,称为朋友类。

上体育课,我们经常有这样一个场景:
体育老师上课前要体育委员确认一下全班女生到了多少位,也就是体育委员清点女生的人数。类图如下:

老师要求清点女生类图

老师类:

public class Teacher{
  //老师对体育委员发一个命令,让其清点女生人数
  public void command(GroupLeader groupLeader){
     List<Girl> listGirls = new ArrayList();
     //初始化女生
     for(int i=0;i<20;i++){
       listGirls.add(new Girl());
     }
     //告诉体育委员开始清点女生人数
     groupLeader.countGirls(listGirls);
  }
}

体育委员类:

public class GroupLeader{
  //清点女生数量
  public void countGirls(List<Girl> listGirls){
     System.out.println("女生人数是:"+listGirls.size());
  }
}

女生类:

publci class Girl{
}

场景类:

public class Client{
   public static void main(Strings[] args){
      Teacher teacher = new Teacher();
      //老师给体育委员发清点女生人数的命令
      teacher.command(new GroupLeader());
   }

}

我们再回头看Teacher类,Teacher类只有一个朋友类GroupLeader,Girl类不是朋友类,但是Teacher与Girl类通信了,这就破坏了Teacher类的健壮性,Teacher类的方法竟然与一个不是自己的朋友类Girl类通信,这是不允许的,严重违反了迪米特原则。

我们对程序进行如下修改,将类图修改如下:

修改后的类图

修改后的老师类:

public class Teacher{
  //老师对体育委员发一个命令,让其清点女生人数
  public void command(GroupLeader groupLeader){
     //告诉体育委员开始清点女生人数
     groupLeader.countGirls();
  }
}

修改后的体育委员类:

public class GroupLeader{
   private List<Girl> listGirls;
   public GroupLeader(List<Girl> listGirls){
      this.listGirls = listGirls;
   }
  //清点女生数量
  public void countGirls(){
     System.out.println("女生人数是:"+listGirls.size());
  }
}

修改后的场景类:

public class Client{
   public static void main(Strings[] args){
     //产生女生群体
     List<Girl> listGirls = new ArrayList<Girl>();
     //初始化女生
     for(int i=0;i<20;i++){
       listGirls.add(new Girl());
     }

      Teacher teacher = new Teacher();
      //老师给体育委员发清点女生人数的命令
      teacher.command(new GroupLeader(listGirls));
   }
}

对程序修改,把Teacher中对Girl群体的初始化移动到场景类中,同时在GroupLeader中增加对Girl的注入,避开了Teacher类对陌生类Girl的访问,降低了系统间的耦合,提高了系统的健壮性。

朋友类间也是要有距离

我们在安装软件时,经常会有一个安装向导的过程。比如第一步确认是否安装,第二步确认License,第三步选择安装目录…..。这个是一个典型的顺序执行动作,我们定义软件安装过程的类图如下:

[图片上传失败...(image-7fde03-1537347431500)]

导向类:

public class Wizard{
   private Random rand = new Random(System.currentTimeMillis());
   //第一步
   public int first(){
       System.out.println("执行第一个方法.....");
       return rand.nextInt(100);
   }
   //第二步
   public int second(){
       System.out.println("执行第二个方法.....");
       return rand.nextInt(100);
   }
   //第三步
   public int third(){
       System.out.println("执行第三个方法.....");
       return rand.nextInt(100);
   }
}

InstallSoftware类:

public class InstallSoftware{
  public void installWizard(Wizard wizard){
     int first = wizard.first();
     //根据first返回的结果,看是否要执行下一步
     if(first >50){
         int second = wizard.second();
         if(second >50){
             wizard.third();
         }
     } 
  }

}

场景类:

public class Client{
   public static void main(Strings[] args){
      InstallSoftware invoker = new InstallSoftware();
      invoker.installWizard(new Wizard());
   }
}

以上的程序非常简单,但是隐藏了一个问题。Wizard类把太多的方法暴露给InstallSoftware类,导致两者的关系太亲密,耦合关系变量异常牢固。我们把Wizard类进行重构,uml类图如下:

[图片上传失败...(image-99973c-1537347431500)]

修改后的Wizard类:

public class Wizard{
   private Random rand = new Random(System.currentTimeMillis());
   //第一步
   private int first(){
       System.out.println("执行第一个方法.....");
       return rand.nextInt(100);
   }
   //第二步
   private int second(){
       System.out.println("执行第二个方法.....");
       return rand.nextInt(100);
   }
   //第三步
   private int third(){
       System.out.println("执行第三个方法.....");
       return rand.nextInt(100);
   }
   //软件安装过程
   public void installWizard(){
      int first = wizard.first();
      //根据first返回的结果,看是否要执行下一步
      if(first >50){
          int second = wizard.second();
          if(second >50){
             wizard.third();
          }
      } 
  }
}

修改后的InstallSoftware类:

public class InstallSoftware{
  public void installWizard(Wizard wizard){
     wizard.installWizard()
  }
}

通过重构,类间的耦合关系变弱了,结构变得清晰,变量的风险也变小了。
一个类公开的public方法和属性越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大。因此,为了保持朋友类间的距离,在设计时需要反复衡量:是否还可以再减少public方法和属性,是否可以修改为private,package-private,protected等访问权限,是否可以加上final关键字。

注意:

迪米特原则要求类“羞涩”一点,尽量不要对外公开太多的public方法和非静态的public变量,尽量内敛,多使用private,package-private,protected等访问权限。

是自己的就是自己的

在实践中经常出现这样一个方法,放在本类中也可以,放到其它类中也可以。那怎么处理呢?你可以坚持一个原则:如果一个方法放在本类中,即不增加类间关系,也对本类不产生负面影响,那就放到本类中。

迪米特原则的实践

迪米特原则的核心观念就是类间解耦,弱耦合,只有弱耦合后,类的复用率才可以提高。其结果就是产生了大量的中转或跳转类,导致系统复杂,为维护带来了难度。所以,我们在实践时要反复权衡,即要让结构清晰,又做到高内聚低耦合。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容