12

让我们从维基百科开始:

更正式地说,函数的得墨忒耳定律要求对象O的方法m只能调用以下类型对象的方法:

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

规则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 条规则吗?


德墨忒耳法则不是暗示链接是不好的吗?

User.getName().getLastName();

这导致高耦合。


“告诉,不要问”不是类似的原则吗?

这就是一切吗?我错了吗?你怎么能遵守得墨忒耳法则?

4

3 回答 3

4

“告诉不要问”有点不同。

得墨忒耳:不要拿东西去拿东西来做最后的事情。

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

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

对比

if (someList.isEmpty()) { bla

在这两种情况下,您都在调用其他对象的方法;但有一个关键区别:第一次调用向您公开了另一个对象的“内部”状态;然后你会做出一些决定。鉴于,在“TDA”改进的第二版中;您将“状态评估”留在该其他对象中;从而以某种方式减少耦合。

但仅作记录:第二个示例仍然根据该列表的状态做出决定。从这个角度来看,它只是比选项 1稍微好一点的版本。理想情况下,您不需要此类检查。

于 2016-03-10T19:58:33.617 回答
2

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

internal class MyConfiguration
{
    private static String MyConfigurationValue; // set in constructor
    MyConfiguration(){ MyConfigurationValue = DoSomethingToLoadValue(); }
    public static String GetMyConfigurationValue(){ return MyConfigurationValue; }
}

在这种情况下(假设设计模式在所有其他方面都可以接受),得墨忒耳法则将允许这样做,因为它是全局可访问的,并且旨在以这种方式进行。

于 2016-03-10T20:03:29.837 回答
2

规则 5 的示例是:

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

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

    public void method2() {
    }
}

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


你的例子:

user.getName().getLastName();

显然违反了法律,因为您从“getName()”获得的对象不会属于列出的任何类别。注意:即使您不使用链式调用,这也是错误的:

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

因为对象“名称”仍然不属于任何列出的类别。

但是这样的事情是可以的:

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

为什么允许这样做?因为您要么每次都返回相同的对象(r​​eportBuilder),要么每次都获得一个新对象,如果构建器被实现为不可变的话。无论哪种方式,它都属于法律 2 或 3,所以无论哪种方式都可以。


您的第三个问题是“如何服从”。好吧,这是一个复杂的问题,但首先,想想什么样的方法实际上是法律禁止的!

只是把定律变成否定的:我们不应该在已经存在的对象上调用方法(因为新对象是例外的),并且不是我的对象,或者我的对象的字段,或者我的参数。这样就剩下其他对象的字段中的对象了!

所以基本上这意味着你不应该能够“获得”对不是你的对象、不在你的领域中的对象以及不是直接参数的对象的访问权限。我将其总结为“没有吸气剂”!

于 2017-05-11T11:21:37.947 回答