Confusion about Law of Demeter

Started by
26 comments, last by Lewis_1986 12 years, 7 months ago

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

[color="#1C2837"][color="#000088"]void [color="#660066"]PhysicsEngine[color="#666600"]::[color="#660066"]CollideWithWorld[color="#666600"]([color="#000088"]const [color="#660066"]GameCharacter[color="#666600"]&[color="#000000"] gameChar[color="#666600"]) [color="#666600"]{ [color="#880000"]//.... [color="#660066"]BoundingBox[color="#000000"] bBox [color="#666600"]=[color="#000000"] gameChar[color="#666600"].[color="#660066"]GetMeshBoundingBox[color="#666600"](); [color="#000088"]bool[color="#000000"] result [color="#666600"]= [color="#660066"]Collide[color="#666600"]([color="#000000"]bBox[color="#666600"]); [color="#880000"]//..... [color="#666600"]}


Yes.

Instead, the classes should be structured like this:class GameCharacter {
void CollideWithWorld() {
bool result = world->Collide(bBox); // or bBox.Collide(world);
}
private:
BoundingBox bbox;
World * world;
};

The original code is too small to make any meaningful distinction though, so the above may not seem like a relevant change and World is not defined, so it might be unsuitable abstraction as well.

In practice, very few libraries use such design, especially those concerned with physics, since it represents worst possible design with regard to hardware architecture (aka, slowest possible).
Advertisement
Ok. Makes sense. But then:

void myClass::foo()
{
....

std::string s2 = s1.substr(1, 3);

char *c = s2.c_str();

char *c2 = global_format(c);

}

Breaks it since the STL could have implemented a function called global_format(const char *) ?

Ok. Makes sense. But then:

void myClass::foo()
{
....

std::string s2 = s1.substr(1, 3);

char *c = s2.c_str();

char *c2 = global_format©;

}

Breaks it since the STL could have implemented a function called global_format(const char *) ?


C and C++ are not OO languages, so such rules cannot apply. It's impossible to discuss such rules in these languages.

STL is not designed to be OO, even though it respects all the useful properties of SOLID principle. Instead of worrying over pointless laws, look at how and why STL is designed the way it is. It's actually one of best applications of OO concepts today.


Also, these are design guidelines, so discussing them in scope of one function is as useful as discussing structural integrity of a building by looking at a pebble in a brick. Design principles apply to interaction between systems. Design patterns resulted in same disaster. Instead of applying them to design concerns, people spent months refactoring single methods just to meet some unapplicable rules.

Instead of applying them to design concerns, people spent months refactoring single methods just to meet some unapplicable rules.
[/quote]

Exactly. Inapplicable rules. And the reason is that such rules are too general and have fragile inconsistent definitions for the sake of confusion, academia kind of work ;)

It could be more practical for languages like Java, JavaScript and C#, where an object is returned by reference, making the internal data of a class accessible even using getters.

As you mention, C and C++ are not OO in a sense they support a broader range of paradigms.

Thanks for all helping me sorting out the confusion, now it's very clear to me :)



It could be more practical for languages like Java, JavaScript and C#, where an object is returned by reference, making the internal data of a class accessible even using getters.


Getters, by very definition, violate the law as well as its spirit. They are the worst abomination that happened to programming after singletons. "Tell, don't ask".

Look at collision example above, no getters.


References and similar details do not apply, they are implementation details which do not affect the OO principles.
Then I would say The Law of Demeter has nothing to do with OO principles, and it has to be language-dependent coding style. PERIOD.


[color=#1C2837][size=2]They are the worst abomination that happened to programming after singletons
[/quote]


After Java and C#. :)

Then I would say The Law of Demeter has nothing to do with OO principles, and it has to be language-dependent coding style. PERIOD.
Unfortunately, it was defined in terms of OO, so arguing about that is somewhat futile.

But as with all methodologies, it could use some toning down in dramatic presentation. Nevertheless, it's an important design concept.

Regarding getters, they really are a mistake and side-effect of hammering SQL databases into enterprise beans. The alternate approach is commonly used:
class Account {
int getAmount();
void setAmount(int newAmount);
};

Account a = ...
int newValue = a.getAmount();
newValue += 100;
a.setAmount(newValue);
Obviously, getters expose internal state. There is something known as amount, which is an intrinsic property of Account.

Instead, we do this:
class Account{
void deposit(int value);
};

Account a = ...
a.deposit(100);
Trivial case, but we no longer know or care about how amount is represented. value would most likely be represented with a class as well.

"Ah, but I need to be able to access amount so I can print it":class Account{
void printValue(OutputStream o);
}
Since representation of amount no longer matters, it might be float, it might be value+currency, it might be coconuts, the above works - only Account knows how it's done.

"Ah, but I need to frobnicate the amount". Again:interface Frobnicator {
int foo(int a);
}

class SeriousFrobnicator implements Frobnicator {
int foo(int a) {
return a + toAdd;
}
private int toAdd;
};

class Account {
void frobnicate(Frobnicator f) {
amount = f.foo(amount);
}
private int amount;
};

Account a = ..
a.frobnicate(new SeriousFrobnicator(100));
Each of these examples doesn't access anything from outside, it uses only its own members, and is open to change.

One potential downside is that certain non-object types are passed as parameters (such as int), which goes against "pure" solution, but doesn't break it.


And eventually you end up with dependency injection and inversion of control, which is a proven way to scale projects. It's somewhat tedious to write (known issue with LoD as well) and it does require considerable number of various helper classes, but it achieves the important goal, namely keeping project loosely coupled.


In practice, recognizing well-designed code is trivial. But LoD might not be the most crucial part. While loose coupling generally is desirable, there can be too much of a good thing and there are several ways to achieve it.
http://en.wikipedia..../Law_of_Demeter, I have never heard of it before (although I have heard about encapsulation and abstraction) and I did know that it is naughty (not wrong) to assume a class has a class that has a function, but basically I think laws like this stifle common sense, it may not be very OOP to occasionally ignore this rule, but I can imagine significant code bloat and mind-boggling if it were universally applied to every single project or class. Having said this the engine->torque example was absolutely exemplorary.

As a side note, in environments such as ECMA, could a person not chain evaluations to nullify the negative effects of such assumptions so:

eval("car57").eval("getEngine()").eval("getTorque()")

As I understand it ECMA script provisions for all objects having internal evaluation and returning NULL (which is an object in ECMA I think) so does this destroy the need for demeters law

Update: Just checked and I was wrong about ECMA, it cannot solve this problem but a solution would be some sort of managed code

This topic is closed to new replies.

Advertisement