views:

179

answers:

3

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?

+3  A: 

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".

JP Alioto
I've never heard of the Law of Demeter before, so... it makes perfect sense that convenience functions which delegate to B would be required, but is "B getB()const" prohibited? If so, why?
Michael Aaron Safyan
That's the whole idea of the law ... it could be restated "You only talk to your immediate friends." What is the benefit? This idea has been around a long time. These days we are writing very loosely coupled applications by course.
JP Alioto
+3  A: 

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.

Steven Sudit
Kinda have to +1 you here since you said the same as me only shorter and quicker :-)
Steve Jessop
Amd I returned the favor since you included a nice example.
Steven Sudit
+8  A: 

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.

Steve Jessop
See, a Uri wrapper is exactly the sort of ubiquitous class that I'd exempt from this rule, just like a string. Seen as a means to an end, this is a good rule of thumb, but it's not an end in itself. The goal is to hide internal implementation details, not to play coy.
Steven Sudit
Yes, I'd have no argument at all if it was called the Guideline of Demeter, but there's no use being timid when trying to <s>herd cats</s> advise programmers. I have no doubt that as an academic exercise, applying it as a Law is interesting and fruitful and gives lots of ideas for improving interface design. IMO you draw the line as soon as you're routinely embedding one interface into another. Sometimes an X actually does just have a Y, and everyone knows it :-)
Steve Jessop
Well said. My concern about <s>insane cats</s> typical programmers is that some have a tendency to view good ideas as magical. The stereotypical example is the recent graduate who reads the GoF book on Design Patterns and then goes off to change EVERYTHING into a Singleton.
Steven Sudit
This is an outstanding answer! Also, this makes everything much clearer. Thanks! :)
Joe