Law of Demeter and How to Work With It
The Law of Demeter is an interesting programming principle. It’s the only one I know of that has a near-mathematical definition:
Any method m of an object O may only invoke the methods of the following kinds of objects:
- O itself
- m‘s parameters
- Any objects created/instantiated within m
- O‘s direct component objects
- A global variable, accessible by O, in the scope of m
In case you didn’t realize, this list doesn’t allow you to call the methods of anything returned by any of the methods allowed. It essentially prohibits chaining of method calls. I’ll give some exceptions to that in a bit.
Why?
What is the purpose of the Law of Demeter? What is it trying to prevent? The Law of Demeter is designed to decrease coupling and increase encapsulation. By following the Law of Demeter, you make it so that the class O doesn’t need to know anything about the source of m other than what m does. O shouldn’t know anything about what m returns, other than its existence, in order to reduce coupling to it.
I may not be explaining it the best. It’s not a bad idea to go out and see how others explain it. The real purpose of this post is to describe ways of working with the law, an anti-pattern, and some exceptions.
Anti-Pattern
We’ll start with the anti-pattern. I call it Layered Private Methods, and it was my first thought about “getting” around the Law of Demeter, but I quickly figured out that, while it technically follows the rules given, it doesn’t follow the reason for the law.
The anti-pattern works like this. Say you originally had the following piece of code that you want to modify to follow Demeter:
public void m(Parameter parameter) { ReturnValue result = parameter.method(); result.method(); }
To fix it, you change it to this:
public void m(Parameter parameter) { otherMethod(parameter.method()); } private void otherMethod(ReturnValue rv) { rv.method(); }
As you can see, each method still follows the law, but the class is still coupled. Whenever you call another method of O from within it, you should consider the code in that called method to be part of m in order to still follow the spirit of the Law of Demeter.
Layers
So what should you do to fix the code? In this case, you can turn the private method call into a call on a new class:
public class NewClass { public void extractedMethod(ReturnValue rv) { rv.method(); } }
So m can look like this now:
public void m(Parameter parameter) { NewClass helper = new NewClass(); helper.extractedMethod(parameter.method()); }
Now, this may seem like a complete waste, creating a new class that is essentially a replacement for a private method. But, in fact, there are many out there that think that every private method should become a new class. While I agree that our private methods do display a tendency toward going beyond the scope of the classes they’re contained within. Whenever you see a private method, consider extracting a class from it.
But, is this the best way to call that class? By now, you should know about dependency injection (look it up if you don’t; it’s super simple, but important). If the calls moved to the classes are complex enough that they merit their own testing, you should apply DI any way you find to be most appropriate. If not, still consider it. There may not be a good reason to do it right away, though, unless you expect to have multiple implementations of it.
Exception 1: Data Structures
When m is a method on a data structure or returns a data structure, then the Law of Demeter can be ignored. A data structure is a class whose entire purpose is based around storing/representing data. This includes the primitive type wrappers, String, “java beans”, and others. It’s understandable that, if definition of data changes, users of the data should have to adapt to the change. If the method you call (that breaks the Law of Demeter) is simply returning public internal data (the data may itself be private, but with public getters), there’s no good reason why not to allow it.
Immutable objects can be part of breaking the law too, with these guidelines: the “bad” method call 1) returns public internal data (like before) or 2) returns a new object of the same type as itself.
Number 2 is safe because the method call is equivalent to incrementing a number. If integers didn’t have the ability to use operators, incrementing would be exactly like number 2 (n = n.increment()
), assuming immutable objects.
Exception 2: The Builder Pattern and Other Fluent APIs
When an API is designed to be fluent (and therefore, usually chained), there’s no good reason to lose the readability just to be a strict Law of Demeter follower. For example, java 8’s Stream
API would be worthless if you didn’t allow yourself to chain methods.
Outro
I hope you now have a better understanding of the purpose and use of the Law of Demeter. Now go and program! And have fun!
Reference: | Law of Demeter and How to Work With It from our JCG partner Jacob Zimmerman at the Programming Ideas With Jake blog. |