Jump to content

  • Log In with Google      Sign In   
  • Create Account


Confusion about Law of Demeter


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
27 replies to this topic

#1 RecycledBytes   Members   -  Reputation: 100

Like
0Likes
Like

Posted 24 August 2011 - 11:01 PM

Does this code violates the Law of Demeter?


void object::foo()
{
        //...

	std::string s1 = "Hello World!";

	string s2 = s1.substr(0, 5);

	// process string s2...
        //.....
}

The function foo() calls a function on object string s1, which returns s2 to be further processed in this function...This looks like violating the Demeter law since it calls functions on an object
returned by another function. If so then what should be the alternative?

Thanks.

Sponsor:

#2 ApochPiQ   Moderators   -  Reputation: 14980

Like
1Likes
Like

Posted 24 August 2011 - 11:54 PM

s1 is stored locally within foo(), so anything that accesses s1's member functions is legit within foo().

A violation would be if you had something like:

struct bletch
{
	bletch(const std::string& contents)
		: Something(contents)
	{ }

	std::string Something;
};

void foo()
{
	bletch b;
	std::cout << b.Something.substr(0, 5);		// Directly accessing a method of bletch's members is forbidden
}

However, the practical applications of the Law of Demeter are often limited. It is much more useful to work in terms of layered abstractions than strict adherence to LoD style. Rather than demanding that you never access nested members, for instance (which can become really impractical in real code), you build everything like layers of lasagna. One layer may access the layer below it, but no layer may access the layer above it. Reaching more than one layer down is permissible but should be minimized when it makes sense.

#3 RecycledBytes   Members   -  Reputation: 100

Like
0Likes
Like

Posted 25 August 2011 - 12:25 AM

Very helpful. Thanks!

#4 Antheus   Members   -  Reputation: 2397

Like
0Likes
Like

Posted 25 August 2011 - 11:24 AM

Short version, don't do this:

foo->bar->baz->sayHello();

foo().getBar().getBaz().sayHello()

Stick to single level of indirection (aka one dot or one arrow).

#5 RecycledBytes   Members   -  Reputation: 100

Like
0Likes
Like

Posted 26 August 2011 - 10:18 PM

Now this triggered another question. If the data member Something is returned by getters as a reference which is the case of managed languages like C# or JAVA, then it's a violation of the Demeter law. However, in case of C++ where we can get a copy of the object it's not. So although both languages used getters, it's the language semantics that determines this kind of violation. Correct?

#6 ApochPiQ   Moderators   -  Reputation: 14980

Like
0Likes
Like

Posted 26 August 2011 - 10:39 PM

Not quite.

foo.GetSomething().bar();	// violation
foo.DoBarOnSomething();		// ok

This holds regardless of language semantics. It doesn't matter what GetSomething() returns (a value, a reference, a const reference) because the very existence of GetSomething() means you're violating the one-degree-of-separation clause of the Law of Demeter.

Remember, it is better to tell an object to do something to itself than to ask it for information so you can do the same operation externally.

#7 Slavik81   Members   -  Reputation: 360

Like
0Likes
Like

Posted 26 August 2011 - 10:40 PM

Incorrect. The Law of Demeter isn't about whether the data you're accessing is a copy or a reference, it's about maintaining a coherent abstraction and limiting the impact of change.

#8 RecycledBytes   Members   -  Reputation: 100

Like
0Likes
Like

Posted 26 August 2011 - 11:10 PM

I'm now more confused. In one of the text books, it says that calling a method on an object not created by the function itself violates the law.

In this case

Something s = obj.GetSomething();

s.DoSomething();

Now if "s" was a copy rather than a reference it would be an object created inside the caller method and hence does not violate the Demeter. Otherwise, it's manipulating "obj's" data member directly.

#9 iMalc   Crossbones+   -  Reputation: 2299

Like
1Likes
Like

Posted 27 August 2011 - 03:34 AM

I'm now more confused. In one of the text books, it says that calling a method on an object not created by the function itself violates the law.

Clearly that definition is not right as it would mean calling anything on a global variable would be a violation.

It's really all about how many dots or arrows you have following from an object or pointer, in an expression. > 1 = bad.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

#10 RecycledBytes   Members   -  Reputation: 100

Like
0Likes
Like

Posted 27 August 2011 - 11:20 AM

Specifically from the "Clean Code," by Robert C. Martin, page 98, where it explains that it's not only about dots...

Something fishy about the law of Demeter???

Any good reference?

#11 jwezorek   Crossbones+   -  Reputation: 1788

Like
1Likes
Like

Posted 27 August 2011 - 11:46 AM

Something fishy about the law of Demeter???

Just that the "Rule of Thumb of Demeter" doesn't sound as cool?

#12 Antheus   Members   -  Reputation: 2397

Like
0Likes
Like

Posted 27 August 2011 - 12:07 PM

Specifically from the "Clean Code," by Robert C. Martin, page 98, where it explains that it's not only about dots...

Something fishy about the law of Demeter???

Nope. As referenced, consultants need to justify their existence by talking endlessly over pointless metrics.

What else are they supposed to do? Actual work? Stuff that matters?

Java people also like to obsess over stuff like that. If they instead tried solving actual problems, 95% of them would be out of work since their codebases would go from 10 million lines to code to 1000.

Then there's academia, stuck firmly inside their ivory tower floating in an UFO on some faraway planet.

#13 RecycledBytes   Members   -  Reputation: 100

Like
0Likes
Like

Posted 27 August 2011 - 02:01 PM

Interesting!

So there's no solid well-established definition or specification of the Law of Demeter.

Some decent texts about OO designs don't even mention it. I only encountered this term in "how to code nicely" texts ;) so this could be a cosmetic vague principle that serves its purpose only in code-readability realm.

#14 SriLumpa   Members   -  Reputation: 198

Like
0Likes
Like

Posted 27 August 2011 - 02:29 PM

Now if "s" was a copy rather than a reference it would be an object created inside the caller method and hence does not violate the Demeter. Otherwise, it's manipulating "obj's" data member directly.


I think it's exactly this. if the Something belongs to obj, then tell obj to do whatever needs to be done with it, so that you don't have to care what the Something is. (as ApochPiq said).
Of course it supposes that you can refactor everything. If you don't have control over obj, you can't do much.

It's about a bit more than just readability. And even then, I wouldn't say "only readability", as it's so important !

#15 Antheus   Members   -  Reputation: 2397

Like
0Likes
Like

Posted 27 August 2011 - 02:36 PM

So there's no solid well-established definition or specification of the Law of Demeter.


It's well-established by academia in many publications.

In practice, the above isn't relevant.

What is relevant is that developers who not only gained design experience but are also able to apply it (aren't constrained by arbitrary management enforced "Best Practices") will write code that builds and runs fast, is understandable and moves project along fast. These are the developers who, among many other factors, might be obeying the law, if it helps them.

Some decent texts about OO designs don't even mention it.


OO is one of most degenerated terms. If it's about any real language, such as .net, Java, PHP or similar, it's not OO. OO is actually well-designed methodology, but sadly no language used today follows it or makes it possible to implement it.


Or put differently. We can discuss details of theoretical OO definition into oblivion. But then:
- language makes it impossible to implement them
- the standard library or other required libraries do not obey them
- platform APIs aren't OO nor can they be mapped to pure OO
- best practices prevents adoption
- it directly violates various code conventions

Nice in theory, but cannot be implement on any real world project.

So over time you take what you can and settle that quality of project is inversely proportional to number of sequential dots. Not ideal, but good enough.

And if this is for some exam, then you will need to regurgitate whatever the lecturer wants to hear, since academia hasn't reached consensus (again, in practice) on what it actually means.

---
Why yes, my Cheerios did taste like piss, why do you ask?

#16 RecycledBytes   Members   -  Reputation: 100

Like
0Likes
Like

Posted 27 August 2011 - 02:46 PM

I think it's exactly this. if the Something belongs to obj, then tell obj to do whatever needs to be done with it, so that you don't have to care what the Something is.



Then it's language-semantics dependent. When I get a copy of Something rather than the actual member, then manipulating does not affect the actual object. However if it's a reference, like in C# or Java, then it's definitely accessing internals of the object.


Consider this scenario:




void PhysicsEngine::CollideWithWorld(const GameCharacter& gameChar)
{
		//....
          
          	BoundingBox bBox = gameChar.GetMeshBoundingBox();
		
 		bool result = Collide(bBox);
		
		//.....
}




Here the game characters have to be decoupled from the physics. The first call: GetMeshBoundingBox does not violate the Demeter Law since I'm passing gameChar to the function.

Now obtaining a copy of the bounding box and passing it to the physics engine functions for further processing which could result in a modification in the bounding box itself for collision resposnse wheich I then pass back to the game character to alter its' own mesh...BUT this could be a bigggggg problem if the BoundingBox returned was a reference, where accessing and manipulating it would result in directly manipulating the gameChar internal objects. So yeah language does make a difference.



#17 iMalc   Crossbones+   -  Reputation: 2299

Like
0Likes
Like

Posted 27 August 2011 - 02:46 PM

Something s = obj.GetSomething();

s.DoSomething();

Now if "s" was a copy rather than a reference it would be an object created inside the caller method and hence does not violate the Demeter. Otherwise, it's manipulating "obj's" data member directly.

I don't think it matters whether it's a copy or a reference. The difference between the above and:
obj.GetSomething().DoSomething();
is that the former is like Alice saying to Bob, "Can you deliver this package to Charlie please?" and then saying to Charlie, "Can you open the package, take out the device, and plug it into your computer please?"
Whereas the later is like saying "Bob, can you please deliver this package to Charlie and tell Charlie to open the package, take out the device, and plug it into his computer?".
The main difference being that in the first case the second instruction is given directly to Charlie, not relayed via Bob, or in the case of the code, the method is called directly on s by the function, and whether s is a copy or a reference makes no difference.
Sorry this is a very weak metaphor.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

#18 swiftcoder   Senior Moderators   -  Reputation: 9851

Like
0Likes
Like

Posted 27 August 2011 - 03:01 PM

I'm now more confused. In one of the text books, it says that calling a method on an object not created by the function itself violates the law.

In this case

Something s = obj.GetSomething();

s.DoSomething();

Now if "s" was a copy rather than a reference it would be an object created inside the caller method and hence does not violate the Demeter. Otherwise, it's manipulating "obj's" data member directly.

Nope. The point here is that 's' is not created by this function, it is created by 'obj' (via the 'GetSomething' method).

In other words, the above code is semantically no different than 'obj.GetSomething().DoSomething()', which violates the double-dot rule.

Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#19 Antheus   Members   -  Reputation: 2397

Like
0Likes
Like

Posted 27 August 2011 - 03:34 PM

In other words, the above code is semantically no different than 'obj.GetSomething().DoSomething()', which violates the double-dot rule.


As said, languages used in practice do not make it viable to follow such strict principles. Here is a well-designed example:

SqlQuery q = SqlQuery.select("*").from("people").where("id == 100");
Plenty of dots, but no problem.

Violation of law comes from making assumptions about structure of called objects. Here we always call on same *interface*, so the depth remains one.

When doing somehting like:
car.getEngine().getTorque()
we make assumptions about engine having torque. This point is blurred due to static typing in most languages. Since we can do compile-time check we know that engine has torque, so everything is seemingly fine. IDEs make a mockery of this even further.

But let's consider a dynamic language or even SmallTalk. getEngine() may return something with torque, something without torque or a cheeseburger. OO does not require strict or static or any kind of typing.

Consider:
eval("foo.php/py/lisp").getTorque()
This statement is fine - eval makes something, then takes torque. But counter example would be:
eval("foo.php/py/lisp").getEngine().getTorque()
Suddenly we simply guess that an engine has torque, which breaks encapsulation. If we were to make such as assumption, then it would need to be getEngineTorque().

In pure OO, everything is object, so eval could return "1", "func()", instance of Engine, instance of RevvedEngine, torque or text. No assumption of anything can be made, unlike common languages today where return would be typed as IEngine, which obviously has getTorque() engine.

Code completion is partly to blame for this since it encourages deep calls resolved during some compile-time declaration. Dynamic languages are, ironically, better at OO than those commonly touted as OO due to lack of such tooling which doesn't lead to degenerate design.


In same vein, casting exhibits same flaws. One should never cast to gain access or use instanceof/typeof/etc... to try to force or guess existence of some trait. If Engine doesn't have getTorque(), but RacingEngine does, casting should be avoided (unless if required to work around language limitations, which is the norm in Java and most everywhere else). But design or implementation that requires such behavior is poorly designed.

#20 RecycledBytes   Members   -  Reputation: 100

Like
0Likes
Like

Posted 27 August 2011 - 03:59 PM

Alrite. So does this code break the law? :)

void PhysicsEngine::CollideWithWorld(const GameCharacter& gameChar)
{
//....

BoundingBox bBox = gameChar.GetMeshBoundingBox();

bool result = Collide(bBox);

//.....
}





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS