Wednesday, August 3, 2016

The Law of Demeter (LoD) flashback

Violations of the Law of Demeter make your code unreadable, hard understandable, overcrowded with extra dependencies and bad scalable...

Low of Demeter

Each unit should have only limited knowledge about other units: only units "closely" related to the current unit
- general formulation
Don't talk to strangers! - short form of law
Means each unit should talk only to its friends.
Use only one dot. - simplified form
This is the tricky one: It's only in common, don't confuse when using 'builders', 'factoryMethods', etc.

A method of an object should invoke only the methods of:

  • the object itself;
  • its arguments;
  • any object it creates/instantiates;
  • any direct component objects/properties.

Another words avoid calling the methods of an object inner member that was returned by calling another method on this object. Again, it's fine to do it if that method creates the new instances or provides copies, so object's internal logic won't be bypassed.


Why does LoD rock?

An awesome example of LoD applying was illustrated by David Bock:
The Paperboy, The Wallet, and The Law Of Demeter (PDF)
Instead of allowing the Paperboy to manipulate with the Customer Wallet, the Paperboy must request to get payment from Customer and the Customer will pay from his own Wallet by himself:

Bad:

Java
// code from some method inside the Paperboy class...
payment = 2.00; // “I want my two dollars!”
Wallet theWallet = myCustomer.getWallet();
if (theWallet.getTotalMoney() > payment) {
  theWallet.subtractMoney(payment);
} else {
  // come back later and get my money
}

Good:

Java
// code from some method inside the Paperboy class...
payment = 2.00; // “I want my two dollars!”
paidAmount = myCustomer.getPayment(payment);
if (paidAmount == payment) {
  // say thank you and give customer a receipt
} else {
  // come back later and get my money
}

First of all it improves not only encapsulation but and an abstraction as well (decreases an amount of dependencies).

Regarding the David Bock example there at least three concrete benefits:

  • The Paperboy code is now 'asking' the Customer for a payment. The Paperboy does not have direct access to the Wallet of Customer.
  • The Wallet class can now change, and the Paperboy is completely isolated from that change. Only Customer will care that he got a new Wallet...
  • The most 'object-oriented' answer is that we are now free to change the implementation of getPayment() method. When the Paperboy comes to the door, our Customer may actually get the two bucks from a jar of change, search between the cushions of his couch, or borrow it from his roommate. All of this can be implemented under the getPayment() method without an impact on other parts.

However Dots counting approach sucks

There no two dots in a row in the example above, which violates LoD:

Java
payment = 2.00; // “I want my two dollars!”
Wallet theWallet = myCustomer.getWallet();
if (theWallet.getTotalMoney() > payment) {
  theWallet.subtractMoney(payment);
}

It just because of theWallet local variable, so LoD would be violated if we get rid of it:

Java
payment = 2.00; // “I want my two dollars!”
if (myCustomer.getWallet().getTotalMoney() > payment) {
  myCustomer.getWallet().subtractMoney(payment);
}

Assume also that u are using 'builder' pattern: It has a few dots, but does it violate the LoD - Nope.

Whatever, the LoD it's not a strict prohibition of doing the things in other way. Its just a good practice, a nice idiom of design patterns or useful suggestion of Demeter. Just keep in mind that the end aim is reducing coupling, not dots!


Final Thoughts

The LoD is about encapsulation of the inner state of an object.
If you give access to an inner object, someone can invalidate an object by bypassing some logic.
Yes, it forces to delegate invocations to inner objects, which might cause a bigger amount of code,
but if u use immutable data or value objects, this kind of encapsulation it's not mandatory for you, cause can be invalidated.

Violation of LoD usually results to passing more info to the objects that they need (cause anyway the needed internal data could be accessed e.g. passing root model instead of particular sub-model), which in the end causes a tight coupling and over-complicates unit tests writing.

P.S. Fun part is when I started to write this post about LoD i've figured out that I had already done it two years ago, so this is just an updated article ^_^


see Also


No comments:

Post a Comment