The Law of Demeter: Do Not Accept Candy From Strangers
Bad code happens when a small change anywhere can affect the whole structure. The Law of Demeter encourages a bottom-up philosophy of programming to avoid this.
Join the DZone community and get the full member experience.Join For Free
The Law of Demeter is another of those laws that is so fascinating, and although not part of the 5 basic SOLID principles, can be considered almost an annex to them.
The idea of this law may seem quite simple and clear: "Do not accept candy from strangers." Its application is cumbersome, and that is why I wanted to explain it here today.
Many times it happens that we have a class that in turn is made up of objects from other classes, and those, in turn, are from other objects. It is normal in object orientation, and it is fine. It seems to mean that we are creating our code from small classes that interact with each other.
So far so good. The problem arises when one of the classes needs to use some of the objects a few levels below. We rolled the blanket to the head and we set about creating getters as if there were no tomorrow. That can be dangerous.
The typical code that smells bad enough is something like this:
What is the problem here? Basically, we are coupling the code to the structure of the classes that make up the call chain.
If that structure changes tomorrow in any of the classes involved, this code is affected. Is that bad? It depends, but generally yes, because this code is very prone to modifications: a single refactoring can result in a change in it.
And that's a clear symptom that the code is not well done.
What is The Law of Demeter?
According to Peter Van Rooijen's explanation of The Law of Demeter:
- You can play by yourself.
- You can play with your own toys (but you can't take them apart).
- You can play with toys that were given to you.
- And you can play with toys you've made yourself.
The Law of Demeter is basically a coupling detection mechanism, and it tells us that our object should not know the entrails of other objects with which it interacts. If we want you to do something, why not ask it directly instead of navigating its structure?
The Law of Demeter says that an object should not know the entrails of other objects with which it interacts in favor of pedantry. I will say that the Law of Demeter is fulfilled when, having a function f of a class C , that function only calls functions of:
- An object created by f.
- An object passed as an argument to f.
- An object stored in C field.
(Definition inspired by what you can find in Robert C. Martin's Clean Code).
In my opinion, you do not have to be extremely strict with these rules, but you have to see when we are really taking advantage of the system to load the rules.
If you are using an elegant class encapsulation, but then you fill it all up with getters, you are bursting that encapsulation.
An easy way to detect when this law is being violated is that you end up with many concatenated calls, but it is not a 100% reliable reason (keep reading until the end and you will see why), and neither is it the only reason.
This code could be written as follows, and would still be invalid:
val x = getX()
val y = getY()
val z = getZ()
So basically, the idea is that if you're accessing the internal structure of another class to call your methods, you're probably violating the law.
How Do I Solve the Violations of The Law of Demeter?
Sometimes I get the feeling that I only give bad news, but there is no single solution to this issue. For me, this law is more of a "code smells" alarm, which tells us that something is not very well built out there.
If you need to access the structure of a class you depend on, it is probably because the responsibilities are not well distributed.
This does not always apply and depends on the type of class we have. We can distinguish two types of different classes: objects and data structures. The first define behavior and the second store state. The former is normal to ask them to do things while the latter is more usual to ask them to give us things.
That is why the application of this law to classes of the second type loses enough validity, and in general, we will not have to worry too much about it.
Knowing that there is no single answer to the question of how to solve this, what options do you have?
1. Add Extra Methods
This is the most obvious option, and the one I recommend least. Instead of having several calls, you let each object make its corresponding sub-calls. You could have something like this:
This in turn would call:
And so on. In some cases, it may be worth it, but normally what you are doing is hiding the problem, not solving it.
A good architecture plays a very important role in the decoupling of the different modules of the software, and therefore will greatly reduce the possibility of violating this law.
Normally in an architecture, each layer will have a series of interfaces with which to communicate, and when thinking about those layers that will hide its implementation, we will be avoiding problems like the ones we have seen before.
3. Better Understand Your Domain
The idea of domain, and in particular of Domain Driven Design, is quite simple. It comes to tell us that our problem can be based on some key concepts that define it, and having them very clear, the whole development process is greatly simplified. Not understanding our domain well can easily lead to not modeling the application well, and therefore many problems of this type will arise.
In addition, Domain Driven Design puts a lot of emphasis on the different types of elements that can help us to model our software, so we will be much clearer and more detailed the topic we discussed before about the types of classes.
Like everything in this world, it is interesting to understand the concept behind this law, but not to take it as something that we can not skip or ignore. It will have to be studied in each case if it makes sense and why.
As a small rule, if you see that you need to access the structure of an object that contains logic, there is surely a more elegant way to do it.
Always make the decisions thinking clearly whether is it likely to generate a coupling problem in future, but do not forget that over-thinking is as bad as the complete lack of it.
Did you know this law? Do you know of any other system to detect and solve it? Tell us in the comments.
Opinions expressed by DZone contributors are their own.