什么是得墨忒耳法则?
What is Law of Demeter?
让我们从维基百科开始:
More formally, the Law of Demeter for functions requires that a method m of an object O may only invoke the methods of the following kinds of objects:
- O itself
- m's parameters
- Any objects created/instantiated within m
- O's direct component objects
- A global variable, accessible by O, in the scope of 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?
Demeter 法则不是暗示链接不好吗?
User.getName().getLastName();
这会导致高耦合。
"Tell, don't ask"不也是类似的道理吗?
这就是全部了吗?我错了什么吗?你怎么能遵守得墨忒耳法则呢?
"Tell don't ask"有点不同。
得墨忒尔:不要为了得到某事而从中得到某事,最后做某事。
TDA:不要从另一个对象中检索 "information" 然后再对此做出决定。简单例子:
if (someList.size() == 0) { bla
对比
if (someList.isEmpty()) { bla
在这两种情况下,您都在调用其他对象的方法;但有一个关键区别:第一个调用向您公开了另一个对象的 "internal" 状态;然后你做出一些决定。鉴于,在 "TDA" 改进的第二个版本中;您将 "status evaluation" 留在另一个对象中;从而以某种方式减少耦合。
但仅作记录:第二个示例仍然根据该列表的状态做出决定。从这个角度来看,它只是比选项 1 稍微 更好的版本。理想情况下,您不需要此类检查。
第 5 个很难用 C# 或 Java 表示,因为它们在技术上不支持全局变量。但是,在原则上相似的设计模式中,您可以拥有例如仅包含全局可访问静态配置值的配置 class,例如 (C#):
internal class MyConfiguration
{
private static String MyConfigurationValue; // set in constructor
MyConfiguration(){ MyConfigurationValue = DoSomethingToLoadValue(); }
public static String GetMyConfigurationValue(){ return MyConfigurationValue; }
}
在这种情况下(假设设计模式在所有其他方面都是可接受的),Demeter 法则将允许这样做,因为它是全球可访问的,并且打算这样做。
规则 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
因为对象 "name" 仍然不属于任何列出的类别。
不过这样也可以:
reportBuilder.withMargin(5).withFooter(10)
.withBorder(Color.black).build();
为什么允许这样做?因为您要么每次都返回相同的对象(reportBuilder),要么每次都返回一个新对象(如果构建器实现为不可变的)。无论哪种方式,它都属于法则 2 或 3,所以无论哪种方式都可以。
你的第三个问题是"how to obey"。好吧,这是一个复杂的问题,但首先,想想法律实际上禁止什么样的方法!
只需将定律变为否定:我们不应该在已经存在的对象上调用方法(因为新对象是豁免的),并且不是我的对象、我的对象的字段或我的参数。这样就留下了 other objects!
字段中的对象
所以基本上这意味着您不应该 "get" 访问不属于您、不在您的字段中以及不直接参数的对象。我总结为 "no getters"!
让我们从维基百科开始:
More formally, the Law of Demeter for functions requires that a method m of an object O may only invoke the methods of the following kinds of objects:
- O itself
- m's parameters
- Any objects created/instantiated within m
- O's direct component objects
- A global variable, accessible by O, in the scope of 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?
Demeter 法则不是暗示链接不好吗?
User.getName().getLastName();
这会导致高耦合。
"Tell, don't ask"不也是类似的道理吗?
这就是全部了吗?我错了什么吗?你怎么能遵守得墨忒耳法则呢?
"Tell don't ask"有点不同。
得墨忒尔:不要为了得到某事而从中得到某事,最后做某事。
TDA:不要从另一个对象中检索 "information" 然后再对此做出决定。简单例子:
if (someList.size() == 0) { bla
对比
if (someList.isEmpty()) { bla
在这两种情况下,您都在调用其他对象的方法;但有一个关键区别:第一个调用向您公开了另一个对象的 "internal" 状态;然后你做出一些决定。鉴于,在 "TDA" 改进的第二个版本中;您将 "status evaluation" 留在另一个对象中;从而以某种方式减少耦合。
但仅作记录:第二个示例仍然根据该列表的状态做出决定。从这个角度来看,它只是比选项 1 稍微 更好的版本。理想情况下,您不需要此类检查。
第 5 个很难用 C# 或 Java 表示,因为它们在技术上不支持全局变量。但是,在原则上相似的设计模式中,您可以拥有例如仅包含全局可访问静态配置值的配置 class,例如 (C#):
internal class MyConfiguration
{
private static String MyConfigurationValue; // set in constructor
MyConfiguration(){ MyConfigurationValue = DoSomethingToLoadValue(); }
public static String GetMyConfigurationValue(){ return MyConfigurationValue; }
}
在这种情况下(假设设计模式在所有其他方面都是可接受的),Demeter 法则将允许这样做,因为它是全球可访问的,并且打算这样做。
规则 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
因为对象 "name" 仍然不属于任何列出的类别。
不过这样也可以:
reportBuilder.withMargin(5).withFooter(10)
.withBorder(Color.black).build();
为什么允许这样做?因为您要么每次都返回相同的对象(reportBuilder),要么每次都返回一个新对象(如果构建器实现为不可变的)。无论哪种方式,它都属于法则 2 或 3,所以无论哪种方式都可以。
你的第三个问题是"how to obey"。好吧,这是一个复杂的问题,但首先,想想法律实际上禁止什么样的方法!
只需将定律变为否定:我们不应该在已经存在的对象上调用方法(因为新对象是豁免的),并且不是我的对象、我的对象的字段或我的参数。这样就留下了 other objects!
字段中的对象所以基本上这意味着您不应该 "get" 访问不属于您、不在您的字段中以及不直接参数的对象。我总结为 "no getters"!