Low of Demeter
- 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:
// 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:
// 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' theCustomer
for a payment. ThePaperboy
does not have direct access to theWallet
ofCustomer
. - The
Wallet
class can now change, and thePaperboy
is completely isolated from that change. OnlyCustomer
will care that he got a newWallet
... - The most 'object-oriented' answer is that we are now free to change the implementation of
getPayment()
method. When thePaperboy
comes to the door, ourCustomer
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 thegetPayment()
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:
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:
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 ^_^
No comments:
Post a Comment