德米特定律和类构造函数

发布于 2024-07-27 20:45:25 字数 224 浏览 1 评论 0原文

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

The Law of Demeter does not prevent passing objects into class constructors. However, it does forbid getting that same object back later and calling a method on it to get a scalar value out. Instead, a proxy method is supposed to be created that returns the scalar value instead. My question is, why is it acceptable to pass an object into a class constructor but unacceptable to get the same object back later and pull a value from it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我们的影子 2024-08-03 20:45:25

因为德米特定律规定,你不应该设计一个对象的外部接口,使其看起来像是由某些具有已知接口的其他对象组成的,以便客户端可以抓住并访问。

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

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

这个想法是为了减少耦合 - 如果没有德米特定律,用户必须知道 HTTPRequest 的接口和 URL 才能获取协议。 有了法律,他们只需要 HTTPRequest 的接口。 并且 HTTPRequest.getProtocol() 显然可以返回“http”,而不需要某些 URL 对象参与讨论。

有时请求对象的用户恰好是创建它的人,因此也使用 URL 接口来传递参数,这一事实既不存在也不存在。 并非所有 HTTPRequest 对象的用户都会自己创建它们。 因此,根据法律有权访问 URL 的客户(因为他们自己创建了 URL)可以这样做,而不是从请求中夺回它。 未创建 URL 的客户端无法创建。

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

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

Because the Law of Demeter says that you should not design the external interface of an object to make it look as if it is composed of certain other objects with known interfaces, that clients can just grab hold of and access.

You pass an object into the constructor to tell your new object how to behave, but it is none of your business whether the object keeps that parameter object around, or keeps a copy of it, or just looks at it once and forgets it ever existed. By having a getMyParameterBack method, you've committed all future implementations to be able to produce that whole object on demand, and all clients to couple with two interfaces instead of one.

For example, if you pass in a URL parameter to your HTTPRequest object's constructor, then that doesn't mean HTTPRequest should have a getURL method which returns a URL object on which the caller is then expected to call getProtocol, getQueryString, etc. If someone who has an HTTPRequest object might want to know the protocol of the request, they should (the Law says) find out by calling getProtocol on the object they have, not on some other object that they happen to know HTTPRequest is storing internally.

The idea is to reduce coupling - without the Law of Demeter, the user has to know the interface to HTTPRequest and URL in order to get the protocol. With the Law, they only need the interface to HTTPRequest. And HTTPRequest.getProtocol() clearly can return "http" without needing some URL object to be involved in the discussion.

The fact that sometimes the user of the request object happens to be the one who created it, and therefore is using the URL interface too in order to pass the parameter, is neither here nor there. Not all users of HTTPRequest objects will have created them themselves. So clients which are entitled under the Law to access the URL because they created it themselves, can do it that way rather than grabbing it back off the Request. Clients which did not create the URL can't.

Personally I think the Law of Demeter as usually stated in simple form, is cracked. Are they seriously saying that if my object has a string Name field, and I want to know whether the Name contains any non-ASCII characters, then I must either define a NameContainsNonASCIICharacters method on my object instead of looking at the string itself, or else add a visitName function to the class taking a callback function in order to work around the restriction by ensuring that the string is a parameter to a function I've written? That doesn't change the coupling at all, it just replaces getter methods with visitor methods. Should every class which returns an integer have a full set of arithmetic operations, in case I want to manipulate the return value? getPriceMultipliedBy(int n)? Surely not.

What it is useful for, is that when you break it you can ask yourself why you're breaking it, and whether you could design a better interface by not breaking it. Frequently you can, but really it depends what kinds of objects you're talking about. Certain interfaces can safely be coupled against vast swathes of code - things like integer, string, and even URL, which represent widely-used concepts.

枕头说它不想醒 2024-08-03 20:45:25

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

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

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

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

JP's answer is pretty good, so this is just a supplement, not a disagreement or other replacement.

The way I understand this heuristic is that a call to A shouldn't break because of class B changing. So if you chain your calls with a.b.foo(), then A's interface becomes dependent upon B's, violating the rule. Instead, you're supposed to call a.BFoo(), which calls b.foo() for you.

This is a good rule of thumb, but it can lead to awkward code that doesn't really address the dependency so much as enshrine it. Now A has to offer BFoo forever, even when B no longer offers Foo. Not much of an improvement and it would be arguably better in at least some cases if changes to B broke the caller that wants Foo, not B itself.

I would also add that, strictly speaking, this rule is broken constantly for a certain group of ubiquitous classe, such as string. Perhaps it's acceptable to decide which classes are likewise ubiquitous within a particular layer of an application and freely ignore Demeter's "Rule" for them.

笛声青案梦长安 2024-08-03 20:45:25

这个想法是你只和你最亲密的朋友交谈。 所以,你不这样做......

var a = new A();
var x = a.B.doSomething();

相反,你这样做......

var a = new A();
var x = a.doSomething(); // where a.doSomething might call b.doSomething();

它有它的优点,因为对于调用者来说事情变得更简单(Car.Start() 与 Car.Engine.Start()),但是你会得到很多小包装方法。 您还可以使用中介模式来减轻这种类型的“违规”。

The idea is that you only talk to your immediate friends. So, you don't do this ...

var a = new A();
var x = a.B.doSomething();

Instead you do this ...

var a = new A();
var x = a.doSomething(); // where a.doSomething might call b.doSomething();

It has it's advantages, as things become simpler for callers (Car.Start() versus Car.Engine.Start()), but you get lots of little wrapping methods. You can also use the Mediator pattern to mitigate this type of "violation".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文