【问题标题】:What is Law of Demeter?什么是得墨忒耳法则?
【发布时间】:2016-06-25 19:42:09
【问题描述】:

让我们从维基百科开始:

更正式地说,函数的得墨忒耳定律要求对象 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 是如果ClassOne 有一个类型为ClassTwo 的私有字段(组件),那么您可以从ClassOne 中的方法调用该字段上的方法。
  • 我知道这是 Java 问题,但所有规则(包括规则 5)都可以在 Kotlin 中定义 gist.github.com/panell/5e1a75746b41f69cf6e2093388e100fc

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


【解决方案1】:

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

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

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

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

对比

if (someList.isEmpty()) { bla

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

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 那么“MyConfigurationValue”字段不应该是公开的吗?
    • @Robiow 不在这个例子中,因为它展示了如何以全局方式提供配置信息,但我们不希望其他类能够更改它,无论是意外还是故意。通常,当您允许读取和修改属性时,有必要公开一个字段(对于那些喜欢“属性”方法的人,我要指出,属性大多只是一种偷偷摸摸的方式,可以说所有成员变量是私有的,即使它们的行为就好像它们是公共的,通过在幕后增加开销以使其公开的方法。
    【解决方案3】:

    规则 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 条法律,所以无论哪种方式都可以。


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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-12
      • 1970-01-01
      • 2012-08-30
      • 1970-01-01
      相关资源
      最近更新 更多