德米特定律和类构造函数

Joe*_*Joe 5 oop law-of-demeter

得墨忒耳定律并不妨碍传递对象到类的构造函数.但是,它确实禁止稍后返回同一个对象并在其上调用方法以获取标量值.相反,应该创建一个代理方法来返回标量值.我的问题是,为什么将一个对象传递给一个类构造函数是可以接受的,但以后获取同一个对象并从中提取一个值是不可接受的?

Ste*_*sop 13

因为Demeter法则说你不应该设计一个对象的外部接口,使它看起来好像是由具有已知接口的某些其他对象组成的,客户端只能抓住并访问.

您将一个对象传递给构造函数以告诉您的新对象如何表现,但是对象是否保留该参数对象,或者保留它的副本,或只是查看它一次并忘记它曾经存在,这不关你的事. .通过使用getMyParameterBack方法,您已经提交了所有未来的实现,以便能够按需生成整个对象,并且所有客户端都可以与两个接口而不是一个接口耦合.

例如,如果您将URL参数传递给HTTPRequest对象的构造函数,那么这并不意味着HTTPRequest应该有一个getURL方法,该方法返回一个URL对象,然后调用者将调用getProtocol,getQueryString等.如果有人谁有一个HTTPRequest对象可能想知道请求的协议,他们应该(法律说)通过调用他们拥有的对象上的getProtocol,而不是他们碰巧知道HTTPRequest在内部存储的其他对象.

这个想法是减少耦合 - 没有Demeter法则,用户必须知道HTTPRequest URL 的接口才能获得协议.根据Law,他们只需要HTTPRequest的接口.并且HTTPRequest.getProtocol()显然可以返回"http"而无需在讨论中涉及某些URL对象.

事实上,有时请求对象的用户碰巧是创建它的用户,因此也是为了传递参数而使用URL接口,既不在这里也不在那里.并非所有HTTPRequest对象的用户都会自己创建它们.因此,根据法律有权访问URL的客户,因为他们自己创建了URL,可以这样做,而不是从请求中取回它.没有创建URL的客户端不能.

就个人而言,我认为通常以简单形式陈述的得墨忒耳法则已被破解.他们是否认真地说如果我的对象有一个字符串Name字段,并且我想知道Name是否包含任何非ASCII字符,那么我必须在我的对象上定义一个NameContainsNonASCIICharacters方法而不是查看字符串本身,否则将visitName函数添加到采用回调函数的类中,以便通过确保字符串是我编写的函数的参数来解决限制问题?这根本不会改变耦合,它只是用访问者方法替换了getter方法.每个返回一个整数的类都应该有一整套算术运算,以防我想操纵返回值吗?getPriceMultipliedBy(int n)?当然不是.

它有用的是,当你打破它时,你可以问自己为什么要打破它,以及你是否可以通过不打破它来设计更好的界面.通常你可以,但实际上它取决于你所谈论的是什么类型的物体.某些接口可以安全地与大量代码耦合 - 例如整数,字符串甚至URL,它们代表了广泛使用的概念.


Ste*_*dit 5

JP的回答相当不错,所以这只是一个补充,并不是反对或其他替代。

我理解这种启发式的方式是,对 A 的调用不应该因为 B 类的更改而中断。因此,如果您使用 abfoo() 链接调用,则 A 的接口将依赖于 B 的接口,从而违反了规则。相反,您应该调用 a.BFoo(),它会为您调用 b.foo()。

这是一个很好的经验法则,但它可能会导致尴尬的代码,这些代码并没有真正解决依赖关系,而是将其铭记在心。现在,即使 B 不再提供 Foo,A 也必须永远提供 BFoo。没有太大的改进,如果对 B 的更改破坏了需要 Foo 的调用者,而不是 B 本身,那么至少在某些情况下可以说会更好。

我还要补充一点,严格来说,对于某些无处不在的类(例如字符串),这条规则不断被打破。也许可以接受的是,决定哪些类在应用程序的特定层中同样普遍存在,并自由地忽略 Demeter 对它们的“规则”。