得墨忒耳法则很容易被绕过?
Law of Demeter can easily be bypassed?
是否总是可以通过创建更多方法来绕过 Demeter 法则?
有人提到这是无效的 (http://wiki.c2.com/?LawOfDemeterIsHardToUnderstand),但它似乎应该有效,因为 Demeter 法则允许向任何参数发送消息。
例如,这违反了得墨忒耳法则(假设m1
不returna1
)但可以重构:
class C1 {
m1 (a1: C2) {
a1.m1().m2() // not allowed
}
}
对m2
的访问可以重构为另一种方法:
class C1 {
m1 (a1: C2) {
m2(a1.m1()) // allowed
}
m2 (a1: C3) {
a1.m2()
}
}
"When your method takes parameters, your method can call methods on
those parameters directly."
我会说它的编写方式留给了解释是否应该考虑整个方法的执行堆栈(对我来说这意味着它是要考虑的)。如果是,则无论增加的间接级别 (m2
) 都是违规行为,如果不是,则代码不违反规则。然而,这只是一个指导方针,对于不了解其服务目标的开发人员没有任何帮助:增加封装和限制耦合。
当然有许多创造性的方法可以创建仍然符合大多数设计原则的规则和定义的糟糕模型,但这显然不是重点。这就像声明 private 修饰符可以很容易地使用反射绕过:是的,但很难错误地做到这一点。
在封装和耦合方面,你贴的代码和a1.m1().m2()
没有区别:C1
耦合到C2
AND C3
是通过获取的C2
。 C2
客户(例如 C1
)需要挖掘它的内部结构这一事实很好地表明它的封装可以改进。
另一个与得墨忒耳法则齐头并进的原则是 Tell Don't Ask 原则,它指出应该告诉对象做什么而不是询问它们的状态。这也有利于更好的封装,从而减少耦合。
需要注意的一个重要事项是,有两种类型的对象,数据结构和 "real objects"(数据和行为)。数据结构(例如 DTO -- getter/setter 包)不受大多数设计原则的约束,例如 Demeter 法则,因为它们的作用仅仅是表示和共享数据。然而,此类数据结构通常出现在系统边界(例如应用程序服务、HTTP API 等),并且在模型的核心中拥有许多此类对象是一种面向对象的代码味道。
是否总是可以通过创建更多方法来绕过 Demeter 法则?
有人提到这是无效的 (http://wiki.c2.com/?LawOfDemeterIsHardToUnderstand),但它似乎应该有效,因为 Demeter 法则允许向任何参数发送消息。
例如,这违反了得墨忒耳法则(假设m1
不returna1
)但可以重构:
class C1 {
m1 (a1: C2) {
a1.m1().m2() // not allowed
}
}
对m2
的访问可以重构为另一种方法:
class C1 {
m1 (a1: C2) {
m2(a1.m1()) // allowed
}
m2 (a1: C3) {
a1.m2()
}
}
"When your method takes parameters, your method can call methods on those parameters directly."
我会说它的编写方式留给了解释是否应该考虑整个方法的执行堆栈(对我来说这意味着它是要考虑的)。如果是,则无论增加的间接级别 (m2
) 都是违规行为,如果不是,则代码不违反规则。然而,这只是一个指导方针,对于不了解其服务目标的开发人员没有任何帮助:增加封装和限制耦合。
当然有许多创造性的方法可以创建仍然符合大多数设计原则的规则和定义的糟糕模型,但这显然不是重点。这就像声明 private 修饰符可以很容易地使用反射绕过:是的,但很难错误地做到这一点。
在封装和耦合方面,你贴的代码和a1.m1().m2()
没有区别:C1
耦合到C2
AND C3
是通过获取的C2
。 C2
客户(例如 C1
)需要挖掘它的内部结构这一事实很好地表明它的封装可以改进。
另一个与得墨忒耳法则齐头并进的原则是 Tell Don't Ask 原则,它指出应该告诉对象做什么而不是询问它们的状态。这也有利于更好的封装,从而减少耦合。
需要注意的一个重要事项是,有两种类型的对象,数据结构和 "real objects"(数据和行为)。数据结构(例如 DTO -- getter/setter 包)不受大多数设计原则的约束,例如 Demeter 法则,因为它们的作用仅仅是表示和共享数据。然而,此类数据结构通常出现在系统边界(例如应用程序服务、HTTP API 等),并且在模型的核心中拥有许多此类对象是一种面向对象的代码味道。