设计原则--迪米特法则(LoD)

什么是迪米特法则:

  迪米特法则(Law of Demeter, LoD)是1987年秋天由lan holland在美国东北大学一个叫做迪米特的项目设计提出的,它要求一个对象应该对其他对象有最少的了解,所以迪米特法则又叫做最少知识原则(Least Knowledge Principle, LKP)。
 
  通俗的讲:一 个类对自己需要耦合或者调用的类应该知道的最少,你类内部是怎么复杂、怎么的纠缠不清都和我没关系, 那是你的类内部的事情,我就知道你提供的这么多 public 方法。

迪米特法则包含四层含义:

(1)只和朋友交流:

//老师类
public class Teacher {
  //老师对学生发布命令, 清一下女生
  public void commond(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());
   }
}

//女生类:没有任何的代码
public class Girl {
}

//我们使用Client来描绘一下这个场景
public class Client {
  public static void main(String[] args) {
     Teacher teacher= new Teacher();
     //老师发布命令
     teacher.commond(new GroupLeader());
   }
}

  首先来看 Teacher 有几个朋友,就一个 GroupLeader 类,这个 就是朋友类,朋友类是怎么定义的呢? 出现在成员变量、方法的输入输出参数中的类被称为成员朋友类, 迪米特法则说是一个类只和朋友类交流, 但是 commond 方法中我们与 Girl 类有了交流,声明了一个 List动态数组,也就是与一个陌生的类 Girl 有了交流,这个不好,那我们再来修改一下,类图还是不变,先修改一下 GroupLeader 类:

//老师类
public class Teacher {
  //老师对学生发布命令, 清一下女生
  public void commond(GroupLeader groupLeader){
     //告诉体育委员开始执行清查任务
     groupLeader.countGirls();
   }
}

//体育委员类
public class GroupLeader {
  //有清查女生的工作
  public void countGirls(){
     List<Girl> listGirls = new ArrayList<Girl>();
     //初始化女生
     for(int i=0;i<20;i++){
       listGirls.add(new Girl());
     }
     System. out.println(" 女生数量是: "+listGirls.size());
   }
}

程序做了一个简单的修改,就是把 Teacher 中的对 List初始化
移动到了 GroupLeader 的 countGrils 方法中,避开了 Teacher 类对陌生类 Girl 的访问, 减少系统间的耦合

(2)朋友之间也有距离:

  人和人之间是有距离的,太远就不是朋友了,太近就浑身不自在,这和类间 系也是一样,即使朋友类也不能无话不说,无所不知。

  在项目中应该都碰到过这样的需求:调用一个 类,然后必须是先执行第一个方法,然后是第二个方法,根据返回结果再来看是否可以调用第三个方法, 或者第四个方法等等。

  实现软件安装过程的第一步做什么、第二步做什么、第三步做什么这样一个过程,我 们来看三个类的源代码,先看 Wizard 的源代码:

//按照步骤执行的业务逻辑类
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);
   }
}

//业务组装类,负责调用各个步骤
public class InstallSoftware {
  public void installWizard(Wizard wizard){
     int first = wizard.first();
     //根据first返回的结果,看是否需要执行second
     if(first>50){
       int second = wizard.second();
       if(second>50){
         int third = wizard.third();
         if(third >50){
           wizard.first();
         }
       }
    }
  }
}

//业务场景调用
public class Client {
  public static void main(String[] args) {
     InstallSoftware invoker = new InstallSoftware();
     invoker.installWizard(new Wizard());
   }
}

  运行结果和随机数有关,我们想想这个程序有什么问题吗? Wizard 类把太多的方法暴露给 InstallSoftware 类了,这样耦合关系就非常紧了,我想修改一个方法的返回值,本来是 int 的,现在修改为 boolean,你看就需要修改其他的类,这样的耦合是极度不合适的, 迪米 特法则就要求类“小气”一点,尽量不要对外公布太多的 public 方法和非静态的 public 变量, 尽量内敛。

//按照步骤执行的业务逻辑类
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 = this.first();
     //根据first返回的结果,看是否需要执行second
     if(first>50){
       int second = this.second();
       if(second>50){
         int third = this.third();
         if(third >50){
           this.first();
         }
       }
     }
   }
}

//业务组装类,负责调用各个步骤 public class InstallSoftware {   public void installWizard(Wizard wizard){     //不废话,直接调用     wizard.installWizard();   } }

Client 类没有任何改变,就不在拷贝了,这样我们的程序就做到了弱耦合,
需要做的是:减少 public 方法,多使用 private、
protected 等访问权限,减少非 static 的 public 属性,如果成员变量 或方法能加上 final 关键字就加上,不要让外部去改变它。
 

(3)是自己的就是自己的:如果一个方法放在本类中,既不增加类之间的关系,也对本类不产生负面影响,那就放在本类中。

(4)谨慎使用Serializable:

  这个问题会很少出现的,即使出现也会马上发现问题。 举个例子来说,如果你使用 R M I 的方式传递一个对象 VO( Value Object),这个对象就必须使用 Serializable 接口,也就是把你的这个对象进行序列化,然后进行网络传输。突然有一天,客户端的 VO 对象修改了一个 属性的访问权限,从 private 变更为 public 了,如果服务器上没有做出响应的变更的话,就会报序列化失败。 这个应该属于项目管理范畴,一个类或接口客户端变更了,而服务端没有变更。

 

文章来源:设计模式之禅

 

posted @ 2021-02-05 09:20  小狗钱钱1  阅读(79)  评论(0)    收藏  举报