The Law of Demeter might be one of the most well defined, useful and concisely written rules of Object-Oriented software development ever. It might also be one of the most often ignored things in our profession other than deadlines.
Let’s take a deep dive into what it says, what it actually means, and how to obey it in letter and spirit.
Why should we obey the Law of Demeter?
There are well known abstract concepts in Object-Oriented programming, like encapsulation, cohesion and coupling, that could be theoretically used to generate clear designs and good code. While these are all very important concepts, they are just not pragmatical enough to be directly useful for development. One has to interpret these concepts, and with that they become somewhat subjective and start to depend on people’s experience and knowledge.
The same can be said for other concepts like the “Single Responsibility Principle“, or the “Open/Closed Principle“, etc. These allow a very wide margin for interpretation, so the practicality, the direct usefulness is therefore diminished.
The genius of the Law of Demeter comes from the succinct and exact definition, which allows for a direct application when writing code, while at the same time almost automatically guaranteeing proper encapsulation, cohesion and loose coupling. The authors of the Law managed to take these abstract concepts and distill the essence of them into a clear set of Rules that are universally applicable to Object-Oriented code.
So what does it say?
The Law in its original form is stated this way:
For all classes C, and for all methods M attached to C, all objects to which M sends a message must be
- M’s argument objects, including the self object or
- The instance variable objects of C
(Object created by M, or by functions or methods which M calls, and objects in global variables are considered as arguments of M.)
In the early days of Object-Orientation objects were supposed to “send messages” to each other, that is how they communicated. So the term “objects to which M sends a message” roughly translates to “objects used by M”, or in a more practical definition “objects on which M calls a method on”.
You might have noticed, that there are some very important additional points made in parentheses after the 2 rules. They seem like clarifications mentioned just in passing, but they are actually additional rules. So let’s reformulate the whole Law, so all points stand independently:
For all classes C, and for all methods M attached to C, all objects to which M sends a message must be:
self
(this
in Java)- M’s argument objects
- Instance variable objects of C
- Objects created by M, or by functions or methods which M calls
- Objects in global variables (
static
fields in Java)
What does it mean?
Well, the law formulates what we are allowed to do in any given method M. So, let’s work backwards and find out what it is exactly what this law prohibits.
Rule #4 states, that all objects created during the call to M, either directly or indirectly, are allowed. So the prohibition must be among objects that already existed when the call began.
These already existing objects must have a reference to them, otherwise nobody would be able to access them, therefore these objects must be referenced from fields (variables) of other objects. Rule #5 allows global objects, so that leaves us with objects in instance variables.
Rules #1, #2 and #3 further allows “self”, M’s parameters and all objects in instance variables of C.
That means that the Law prohibits “sending a message” to any already existing object that is held in instance variables of other classes, unless it is also held by our class or passed to us as method parameters.
Examples
Let’s look at some examples of the Law in action:
public final class NetworkConnection { public void close() { ... sendShutdownMessage(); // Allowed? } private void sendShutdownMessage() { ... } }
The method call sendShutdownMessage()
is obviously allowed, because it is covered by Rule #1. Any method can be called on the current object.
How about this one:
public final class NetworkConnection { private Socket socket; public void close() { socket.close(); } }
That is also allowed, because of Rule #3 (as socket
is an instance variable of this class).
But, what about this one:
public final class NetworkConnection { public void send(Person person) { sendBytes(person.getName().getBytes()); sendBytes(person.getPhoto().getBytes(); ... } }
This is a violation of the Law of Demeter. The method received the parameter person
, so all method calls on this object are allowed. However, calling any methods (in this case getBytes()
) on the object returned by either getName()
or getPhoto()
is not allowed. Assuming standard getters, these objects are already existing objects in other objects’ instance variables, therefore they are exactly the kind of objects this method should not have access to.
Chain Calls
Some explanations of the Law concentrate on so-called “chain calls”, that is, expressions which contain multiple dereferencings. Or stated plainly, multiple “dots”, like this:
car.getOwner().getAddress().getStreet();
This is almost certainly a violation of the Law, since all those objects (owner, address) are presumably already existing instance variable values, to which the Law prohibits access.
However, it is not the chain-calling that makes the above a violation, but the access to certain objects. The following “refactored” code still violates the Law:
Owner owner = car.getOwner(); Address ownerAddress = owner.getAddress(); Street ownerStreet = ownerAddress.getStreet();
As above, the objects in owner
, ownerAddress
and ownerStreet
are still not covered by any of the rules, therefore no methods should be called on them.
Fluent APIs
Some interpretations of the Law argue, that since chain-calls are not allowed, fluent APIs are also forbidden. Fluent APIs are created to allow syntactically easy usage of a library or set of classes. They look like this:
Report report = new ReportBuilder() .withBorder(1) .withBorderColor(Color.black) .withMargin(3) .withTitle("Law of Demeter Report") .build();
To determine whether such a construct is allowed, we just have to check each method call to see whether it is specifically allowed. The ReportBuilder
object is created in this method right now, so the first method call withBorder(1)
is on a freshly created object. This is explicitly allowed by Rule #4.
All the following method calls, including the last build()
call are all using the returned objects from the previous call. What is the return value of all these methods? Well, most of the fluent APIs just return the same object over and over again. That is still covered by Rule #4, because the builder is an object that was created in this code. Some fluent APIs use immutable objects, and return a new object every time. But even if this is the case, Rule #4 still applies to indirectly created objects too.
So the Law of Demeter does not prohibit fluent APIs.
“Wrapper” Methods
Some, including the Wikipedia Article on the Law of Demeter argue that the Law implies the usage of “Wrapper” methods to get around calling methods on foreign objects, like this:
car.getOwner().getAddress().getStreet(); // This is a violation car.getOwnerAddressStreet(); // This is a proposed "solution"
There are a couple of problems with this approach. For example: how is getOwnerAddressStreet()
implemented? Presumably like this:
public final class Car { private final Owner owner; public Street getOwnerAddressStreet() { return owner.getAddressStreet(); } ... }
So 2 new methods are introduced, but there was no real structural or design change. One could argue, that while the Law was technically followed, the spirit of the Law was still violated.
The structure is still visible in the method name, and we all know that the caller wants to get the street of the address of the owner of the car.
The question is: why does the caller want that, and how does he/she even know that this information exists at this point? This is when applying the Law blends together with Object-Oriented Design. Obviously the purpose of the Law is not to roll additional hurdles in our way and make our lives more difficult. The same way that our job is not to make sure that we technically follow all the Rules, without thinking about our overall design.
What the Law is quite clearing saying is, that you shouldn’t access the Street in this structure. As in, you shouldn’t even know it’s there. Don’t trick the definition by introducing wrapper methods! There is a deeper design issue here that needs to be addressed (no pun intended)!
Pure “Data Structures”
In his book “Clean Code“, Robert C. Martin has a short section on the Law of Demeter, where he makes two interesting points. One is that perhaps by using direct access to variables, the Law could be circumvented. So instead of this:
car.getOwner().getAddress().getStreet();
which is not acceptable, it could be transformed into the following, which complies with the Law, since it doesn’t involve method calls:
car.owner.address.street; // Using direct access to fields
There are two arguments against this line of reasoning. One is, that it still just tries to work around the Law instead of heeding its advice. And the second is, that direct access to fields arguably still qualifies as “sending a message”, since it is still just communication between two objects.
There is a more nuanced point made by Uncle Bob regarding the above, that the Law of Demeter should not apply to pure data structures anyway.
Pure data structures (“objects” that have no behavior just data) are integral building blocks of a procedural, functional or a mixed paradigm approach. As such, they are of course exempt from having to comply with the Law of Demeter, which is a Law for Object-Oriented development.
Getters
Most, if not all of the negative examples for the Law of Demeter given involve “getter” methods. These are methods like this:
public final class Car { private final Owner owner; public Owner getOwner() { return owner; } }
It is not a coincidence that this is the case. Let’s imagine another method, which uses this getter:
public final class Garage { public void isAllowed(Car car) { Owner owner = car.getOwner(); // Allowed? ... } }
Is the above call even allowed? Well, technically yes. The car
object is a direct parameter to the method, so I may call methods on this object.
However, let’s think about what can be done with the result of this call. The method returns an owner
object, which is neither a parameter nor an object that the Garage
has direct access to. Therefore there can be no further method calls on this object. Not toString()
, not hashCode()
, nothing!
This might be considered unexpected. So that means while calling the getter itself might be technically legal, we can’t actually use the result. It seems getter methods violate the Law of Demeter almost by design.
As previously said, this is neither a coincidence nor is it unintentional. In the Object-Oriented paradigm objects are supposed to tell other objects what to do, delegate, instead of querying data from others and doing everything themselves.
When to apply
There are some opinions in blog posts and other articles expressing that the Law of Demeter is less of a “law” and only a “suggestion” or a “guideline”. Or that it is fine in theory, but does not apply to certain objects, like data structures, Java Beans, entities, business objects, value objects, data transfer objects, or objects for presentation or persistence.
An Object-Oriented developer should be aware, that more often than not these opinions come from a background of different programming paradigms, in which the Law of Demeter might very well have a different weight or even interpretation than in Object-Orientation.
The Law of Demeter is the law of the land in Object-Oriented development. As the original paper itself calls it, it is the Law of Good Style. Complying with the Law technically and in spirit everywhere is the minimum required to produce proper, well designed Object-Oriented code.
Conclusion
The Law of Demeter is a very well defined set of rules for Object-Oriented development. Following these rules in letter and spirit takes little effort if one is applying good Object-Oriented Design principles anyway.
Furthermore it produces clear signals if the code is deviating from the Object-Oriented path, therefore it is an invaluable tool to keep our designs and code style on the right track.
So, what would be a good example to cleanup this code part?
public final class NetworkConnection {
public void send(Person person) {
sendBytes(person.getName().getBytes());
sendBytes(person.getPhoto().getBytes();
…
}
}
Having a function on person saying getNameInBytes is not very nice like you said. Thus if I would require bytes in that context, would I have to implement a converted in my own class saying something like:
“`
sendBytes(extractBytesFrom(person.getName());
sendBytes(extractBytesFrom(person.getPhoto());
“`
Which would contain some logic that converts the given value into a byte stream? OR! Design the Person class in a different way which lets the user decide further?
LikeLike
There is no single correct way to resolve the issue, since everything depends on the business-case that you are trying to implement.
One solution would be, that the Person has a “getBytes()” method, serializing the whole Person, not just wrapping methods, but actually hiding the internal structure of a Person. The caller would not know what fields or what data would be included in the byte array, just that it has a byte array representation.
If the Person is part of a network protocol or similar (so the NetworkConnection is part of the business domain), than the Person could have a “send(NetworkConnection)” method, where it can send itself through a network connection.
There are probably other solutions. The key is, that the outside caller should not have knowledge about the internal structure of the Person. That is what the Law of Demeter tries to say.
LikeLiked by 1 person
> There are probably other solutions. The key is, that the outside caller should not have knowledge about the internal structure of the Person. That is what the Law of Demeter tries to say.
Here’s the difficulty I have with this.
The person’s data is now private. We obey the tell-don’t-ask principle and make it so the person sends its data over the wire through the network connection. The person might not even send their whole self over the wire, perhaps they have an address property, and to properly follow the law of Demeter, the person hands off a NetworkConnection instance over to the address instance to let it send itself over the wire, before the person finishes sending themself.
Great! There isn’t a single other class in the entire repository who’s able to get an instance of Person and probe it for information. Our person class is fully encapsulated! Except, not really. In the end, we’re still sending a data-bag over the wire for some other program(s) to grab and use. Doesn’t it feel inconsistent that the end-user is allowed to get more information about our program from a single API endpoint, than what any individual class is allowed to get? If the data is “public knowledge” to the API users, is it that bad for it to also be public knowledge within the source code itself as well? If it is bad, why?
LikeLike
Your first paragraph is exactly right. That’s the way it would be compatible with the LoD.
Answering “we’re still sending a data-bag over the wire for some other program(s) to grab and use”: So the LoD is obviously only concerned with code. Objects messaging each other. As soon as we leave this context, sending data on the network, to the screen, to the printer, etc., it no longer applies directly. This is what in functional programming would be called a “side-effect” of our code.
*Should* it apply though? At least some version of it? Yes, I think it should. And indeed this is what “microservices” were supposed to do. Microservices were supposed to “encapsulate” the data the same way objects do, implement behavior on them, same as objects do, and function independently from others as far as possible, just like objects do. In essence, we can take the LoD and expand it to apply more broadly to architecture, not just software design, if we want.
Getting back to the point about “end-users” knowing more than “our” objects. So if you follow a microservices approach exposing behavior instead of data, you’re doing exactly the thing objects would do internally. You’re not actually exposing anything more than your internal objects would be exposing.
I know, this seems to contradict how most projects would work and also how most microservices are implemented. Let’s face it, most microservices are a thin CRUD wrapper around a database table these days. This is nonetheless how these very basic rules of object-orientation apply. This is the only way to produce maintainable software, that we know of, exactly how the original LoD paper describes.
LikeLike
Ah, interesting line of thought there, with having microservices themselves follow the tell-don’t-ask principle. I’ve never really thought about that.
LikeLike
If you don’t mind, I’d like to probe you a little bit more about this topic, as I’m interested in understanding it better.
So, one of the principles that the law of Demeter encourages is the “tell don’t ask” principle. One thing I’m realizing about this tell-don’t-ask principle is that, if I understand it correctly, every method that follows this principle must be impure. After all, if you call a pure method, the only thing the method would be able to do is return some information, i.e. we “ask”ed the instance something and it responded. to do a “tell”, we effectively need to have the method go and render to a UI, or store something in the database, etc, instead of returning information to let someone else do it.
This means, a project that’s strictly following this principle will never publicly expose a pure function. Every time you call a method, some sort of side-effect must take place.
Does this line of reasoning sounds correct?
LikeLike
Your reasoning is a little off.
You *can* return things. You can even return an IO Monad of something, which would make the method completely pure even if it needs side-effects, and still potentially be LoD compatible.
What the LoD is essentially saying though, is that you can’t return something that already existed before the call. So you can for example, have a “generateReport()” call, which would return a “Report”. But instead of that being a data structure (which would eventually lead to LoD violations), that object in turn also has things it can do. For example “Report.render()”, or “Report.writeToFile()” or whatever. Even those methods might return something else, for example “Report.render(): Html”, and in turn “Html” again has lower level methods.
So, you *can* actually return data. It’s just that you have to return the functionality that goes with that data too. Essentially, you have to return a new and proper object. Note: that “data” in that object may be older actually, but the object (i.e. the context) needs to be new.
Does that makes sense?
LikeLike
Yeah, it does help, thanks.
I guess I had incorrectly thought that you already wanted to apply tell-dont-ask 100%, and was happy that LoD helped users to actually do it.
But, perhaps it’s the other way around, where it’s not possible to apply tell-dont-ask 100%, and LoD helps you know when it is and isn’t appropriate to follow it.
LikeLike
Engaging article on the topic. The point about the value of how straight forward it is to apply is well made. And you made clear the goal of applying it. But I’m left feeling deep resistance to accepting it as a Law and applying it rigidly. The Person example, with the suggestion of pushing network logic into the data class, and by implication every data class that might need to be sent, is really problematic. You’ve satisfied the law but distributed a single piece of the code out to to every class. On a case by case basis you can probably handle this with some sort aspect or a Visitor and Service Provider Interface combination but …. dang right now I just can’t off hand dismiss the folks that say there needs to be a loophole around complex data structures.
LikeLike
Thanks. I understand your point I think. I was introduced to “object-orientation” and Java with Java Enterprise, and I found it painful and hard to “unlearn” the things that I did there. It took many years for me at least.
So, the first things is, there are no “data classes” in object-orientation. Data is a secondary concern, the primary concern being “behaviour”. This applies to everything: presentation, persistence and even network-related classes/objects. A behaviour of a request can be, that I can send it over the network (like the Person above), and the behaviour of some response might be that it applies some change to other objects. In neither case should the data be visible in these objects. It simply does not matter to other objects what data is exchanged over the network.
I know it sounds different from what we’re used to, and I can only recommend to just try it and see what happens.
For example, the ability to really change the internals (fields) of an object, without having to worry about consequences outside of the object, and without having to thread the change through various layers is absolutely worth it for me.
LikeLike
I agree completely with the theory, but struggling with the reality of practice when applied. The reality I hit is, with many commercial applications, particularly where an RDBMS is handling the persistence, you do end up with a lot of “classes” that are just really old school structs (aka beans, aka tuples). And there is a great deal of composition of these simple objects. There are plenty of patterns for dealing with the object relational impedance miss match but usually they involve compromising paradigm purity one way or another. And as we begin to weave in functional styles too … well yeah I just feel that I wouldn’t want this “Law” as a SCM hook blocking commits…
LikeLike
This article made me remember an article by Allen Holub on Javaworld many years ago.
In “Why getter and setter methods are evil” Holub describes how design can evolve without the use of getters and setters. I liked the idea back then as much as I do now, but I always fall back on dotting my way through.
I think the design has to take a much more central role if one is to succeed with the law of Demeter. It surely does not come by itself.
Do you have any recommendations on books to read if you want to become better at this?
LikeLike
Good question, unfortunately I did not manage to find any good books yet on this subject specifically. While there are a lot of good bits to be found from the usual suspects (Fowler, Evans, Robert Martin, etc.), I have the feeling that object-orientation is just not high on their list of things to worry about.
In the case of Robert Martin, he causally mixes paradigms all the time without much concern, justification or warning, so I would perhaps recommend against using Uncle Bob material when possible, or at least be very careful about it.
David West’s Object Thinking is on my list, I haven’t had the chance to read it yet, but I heard good things about it.
About the differences: You’re right, the design plays a central role. My experience is that “previously” (not being serious about object-orientation) we could code weeks without having to actually think about the business functions we wanted. Just create the data objects (the anemic “Model”), create DAOs, DTOs, UI. All of that requires no knowledge whatsoever about the business domain, only about what data it is about. All logic could be created later, because all data can be accessed from anywhere and everywhere.
Object-orientation (and the Law of Demeter) forces us however to think about business responsibilities right from the very first object (class) we create. Every object and method should be traceable to some parts of the requirements. Not the specification, but the business requirements! For this to work, a much deeper knowledge of the business domain is necessary, and that is just not easy.
LikeLike
Elegant Objects by Yegor Bugayenko is a pretty unorthodox but great book on the topic.
LikeLike
Hi,
just wanted to tell you that I really enjoyed reading your article. Thanks a lot for this nice sum up.
– SJ
LikeLiked by 2 people
Thank you very much for the article, but reading phrases like “let’s take a deep dive…” makes my skin crawl. Please consider dropping abhorrent middle-management buzzwords from technical articles
LikeLike
I love reading your articles on object oriented programming. Please keep it up.
When looking at your tictactoe app, I saw this:
view.show(cells -> ui.askForMove().selectFrom(cells).mark());
(https://github.com/robertbraeutigam/tictactoe/blob/master/src/main/java/com/github/robertbraeutigam/tictactoe/human/HumanPlayer.java)
Doesn’t it violate the Law? It seems to violate it by definition, though perhaps not in spirit. Returned objects keep returning other objects, but only as public interfaces, so perhaps it makes sense that we can know what we can ask of these objects? But if so, then maybe the Law needs to be amended to say something about returning public interfaces?
LikeLiked by 1 person
Thanks for your encouragement, much appreciated! Regarding your question about the TicTacToe app:
In short: that line does not violate the LoD, neither by definition nor in spirit. To verify that, let’s check each call sequentially whether it is allowed by the LoD.
Is “view.show()” allowed? Yes, because “view” is an object I know about. It is is one of HumanPlayer’s instance variables, so I can call methods on it. (Rule #3)
Is “ui.askForMove()” allowed? Yes, because “ui” is another instance variable in HumanPlayer, therefore I can call methods on it. (Rule #3 again)
Is “askForMove().selectFrom()” allowed? Yes, because the result of “askForMove()” returns a completely new object each time. It returns a move which did not previously exist. It is both semantically and actually a new object, so it is covered by Rule #4.
Is “selectFrom(cells).mark()” allowed? Yes, because “selecctFrom(cells)” returns a cell from “cells”, and “cells” is a parameter to my method, or in this case my lambda expression. It is therefore covered by Rule #2.
LikeLike
Ah, yes, you’re right. I missed the part in Rule #4 where it says you can send messages to newly created objects. It makes sense, now that I think about it. Thanks for the reply.
LikeLike
Great article!!
LikeLike