Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Law of Demeter and Class Constructors

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?

like image 683
Joe Avatar asked Jul 20 '09 01:07

Joe


People also ask

What is the Law of Demeter trying to prevent?

The Law of Demeter asks us to minimize coupling between classes and avoid reaching out to the third object in order in order to make refactoring and developing new features easily.

What is a result of adhering to the Law of Demeter principle?

High Cohesion (HC): Adhering to the Law of Demeter often results in additional methods that mirror methods of aggregated objects. As these objects have other responsibilities, the additional methods have fewer commonalities with the “real” methods of the class, which results in a lower cohesion.

What violates the principle of least knowledge or Law of Demeter?

The Law of Demeter principle states that a module should not have the knowledge on the inner details of the objects it manipulates. In other words, a software component or an object should not have the knowledge of the internal working of other objects or components.

Which proposed the Law of Demeter?

History. The law dates back to 1987 when it was first proposed by Ian Holland, who was working on the Demeter Project. The Demeter Project was the birthplace of a lot of AOP (Aspect Oriented Programming) principles.


2 Answers

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.

like image 114
Steve Jessop Avatar answered Oct 16 '22 15:10

Steve Jessop


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.

like image 24
Steven Sudit Avatar answered Oct 16 '22 13:10

Steven Sudit