德米特定律是什么?

时间:2016-03-10 19:20:06

标签: java c# oop law-of-demeter

让我们从维基百科开始:

  

更正式地说,函数的Demeter法要求对象 O m 方法只能调用以下类型对象的方法:

     
      
  1. O本身
  2.   
  3. m参数
  4.   
  5. 在m
  6. 中创建/实例化的任何对象   
  7. O的直接组件对象
  8.   
  9. 一个全局变量,可由O访问,范围为m
  10.   

规则1:

public class ClassOne {

    public void method1() {
        method2();
    }

    public void method2() {

    }
}

规则2:

public class ClassOne {

    public void method1(ClassTwo classTwo) {
        classTwo.method2();
    }
}

class ClassTwo {

    public void method2() {

    }
}

规则3:

public class ClassOne {

    public void method1() {
        ClassTwo classTwo = new ClassTwo();
        classTwo.method2();
    }
}

class ClassTwo {

    public void method2() {

    }
}

规则4(感谢@juharr):

public class ClassOne {

    private ClassTwo classTwo;

    public void method1() {
        classTwo = new ClassTwo();
        classTwo.method2();
    }
}

class ClassTwo {

    public void method2() {

    }
}

规则5:

?

任何人都可以帮我处理规则5吗?

Demeter的法律是否意味着链接不好?

User.getName().getLastName();

这导致高耦合。

Isn&#t; tt;告诉,不要问"一个类似的原则?

这一切都是这样吗?我错了什么?你怎么能遵守得墨忒耳的法律?

3 个答案:

答案 0 :(得分:4)

"告诉不要问"有点不同。

Demeter:在最后的事情上,不要从中得到一些东西来做点什么。

TDA:不要检索"信息"从另一个对象然后做出决定。简单的例子:

if (someList.size() == 0) { bla

VS

if (someList.isEmpty()) { bla

在这两种情况下,您都在某个其他对象上调用方法;但是有一个关键的区别:第一个电话会曝光"内部"你的另一个对象的状态;然后你做出一些决定。然而,在" TDA"改进了第二版;你离开那个"状态评估"在另一个对象中;从而以某种方式减少耦合。

但仅仅是为了记录:第二个例子仍然根据该列表的状态做出决定。从这个角度来看,它只是一个略微比选项1更好的版本。理想情况下,你不需要这样的检查。

答案 1 :(得分:2)

第五种很难在C#或Java中表示,因为它们在技术上不支持全局变量。但是,在原则上类似的设计模式中,您可以使用例如一个只包含全局可访问的静态配置值的配置类,例如(C#):

class VAR1 : public Variable { };
class VAR2 : public Variable { };

System<VAR1, VAR2> s{ /* init values: ("VAR1", 10), ("VAR2", 20) */};

在这种情况下(假设设计模式在所有其他方面都是可接受的),Demeter法则允许这样做,因为它是全球可访问的并且打算这样。

答案 2 :(得分:2)

规则5的一个例子是:

public class ClassOne {
    public void method1() {
        classTwo.STATIC_INSTANCE.method2();
    }
}

class ClassTwo {
    public static final ClassTwo STATIC_INSTANCE = ...;

    public void method2() {
    }
}

Enums基本上都是这样工作的,访问枚举是可以的。

你的例子:

user.getName().getLastName();

显然与法律相矛盾,因为你得到的对象&#34; getName()&#34;不会属于列出的任何类别。注意:即使您没有使用链式调用,这也是错误的:

Name name = user.getName();
name.getLastName(); // <- this is still wrong

因为对象&#34;名称&#34;仍然不属于任何列出的类别。

但是这样的事情还可以:

reportBuilder.withMargin(5).withFooter(10)
    .withBorder(Color.black).build();

为什么允许这样做?因为每次都要返回相同的对象(r​​eportBuilder),或者每次将构建器实现为不可变时都可能是新对象。无论哪种方式,它都属于法律2或3,所以无论如何都可以。

你的第三个问题是&#34;如何服从&#34;。嗯,这是一个复杂的问题,但是刚开始,想想法律实际上禁止哪种方法!

只是将法律置于否定之中:我们不应该在已经存在的对象上调用方法(因为新对象是免除的),而不是我的对象,或者我的对象的字段,或者我的参数。这样就会留下其他对象字段中的对象!

所以基本上这意味着你不应该能够&#34;得到&#34;访问不是您的对象,而不是您的字段,而不是直接参数。我总结为&#34;没有吸气者&#34;!