德米特定律和类构造函数

时间:2009-07-20 01:20:11

标签: oop law-of-demeter

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

3 个答案:

答案 0 :(得分: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,它们代表了广泛使用的概念。

答案 1 :(得分:4)

这个想法是你只跟你的直接朋友说话。所以,你不这样做......

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()),但是你得到了很多小的包装方法。您还可以使用Mediator pattern来缓解此类“违规行为”。

答案 2 :(得分:4)

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

我理解这种启发式的方式是,由于B级改变,对A的调用不应该破坏。因此,如果使用a.b.foo()链接调用,则A的接口将依赖于B,违反规则。相反,你应该调用a.BFoo(),它会为你调用b.foo()。

这是一个很好的经验法则,但它可能会导致尴尬的代码无法真正解决依赖关系,而不仅仅是为了它。现在A必须永远提供BFoo,即使B不再提供Foo。没有太大的改进,至少在某些情况下,如果对B的更改打破了想要Foo的来电者,而不是B本身,那么可能会更好。

我还要补充一点,严格来说,这条规则对于某些无处不在的群体(例如字符串)不断打破。或许可以决定哪些类在应用程序的特定层中无处不在,并且可以自由地忽略Demeter的“规则”。