I came back to work from vacation yesterday, and in our daily standup, my teammates mentioned they were refactoring all of the model objects in our java code to remove all getters and setters and make the model fields all public objects instead, invoking the Law of Demeter as the reason for doing so because
to facilitate the our adherence to Demeter's law: a module should not know about the innards of the 'objects' it manipulates. Since data structures contain no behavior, they naturally exposes their internal structure. So in that case, Demeter does not apply.
I admit I had to brush up on my knowledge of the LoD, but for the life of me I can't find anything to indicate that this is within the spirit of the law. None of the getters/setters in our models contain any business logic, which is his justification for doing this so clients of these objects don't have to understand whether or not there is some business logic being executed within the get/set methods.
I think this is a misunderstanding of what it means to need 'internal knowledge of an objects structure', or at the very least is taking it too literally and breaking a pretty standard convention in the process.
So my question is, does it actually make any sense to expose model objects internal structure directly instead of via getters/setters in the name of the LoD?
The Law of Demeter about the working with objects, not data structure, in your case DTO as I understand.
The Law of Demeter explains that you can call methods of objects that are:
Data models represent containers with some data inside them that should be shown outside. It's their role and they haven't some other behavior besides this.
It doesn't seem to me that this change has anything to do with the Law of Demeter. The law is, essentially, about encoding the structure of your object graph into your code by having methods call through an entire chain of other objects. For example, suppose, in a car insurance application, that a customer has a policy, and a policy has vehicles, and vehicles have drivers assigned to them, and drivers have birth dates and, thus ages. You could imagine the following code:
This would violate the Law of Demeter because this code now has knowledge of internals that it doesn't need to know. It knows that drivers are assigned to vehicles, instead of just being assigned to the insurance policy as a whole. If, in the future, the insurance company decided that drivers would simply be on the policy, instead of being assigned to particular vehicles, then this code would have to change.
The problem is that it calls a method of its parameter,
getPolicy()
, and then another,getVehicles()
, and then another,getDrivers()
, and then another,getAge()
. The Law of Demeter says that a method of a class should only call methods on:(The last one can be a problem for unit testing, where you may want to have objects injected or created by factories rather than created directly locally, but that's not relevant to the Law of Demeter.)
To fix the problem with
hasUnderageDrivers()
we can pass in thePolicy
object and we can have a method onPolicy
that knows how to determine whether the policy has underage drivers:Calling one level down,
customer.getPolicy().hasUnderageDrivers()
, is probably okay — the Law of Demeter is a rule of thumb, not a hard-and-fast rule. You also probably don't have to worry much about things that aren't likely to change;Driver
is probably always going to continue to have a birth date and agetAge()
method.But returning to your case, what happens if we replace all these getters with public fields? It doesn't help with the Law of Demeter at all. You can still have the exact same problem as in the first example. Consider:
(I've even converted
driver.getAge()
todriver.age
, although that would probably be a calculation based on the birth date rather than a simple field.)Notice that the exact same problem with embedding knowledge of how the object graph is put together (a customer has a policy which has vehicles which have drivers) is present when we write the code with public fields instead of getters. The problem has to do with how the pieces are put together, not with whether getters are being called.
Incidentally, the normal reason to prefers getters over (final?) public fields is that you may need to put some logic behind them later on. An age is replaced with a calculation based on the birth date and today's date, or a setter needs to have some validation (throws if you pass
null
, for instance) associated with it. I haven't heard the Law of Demeter invoked in that context before.There is a book called Clean Code by Robert Martin that covers this.
In Chapter 6 (Objects and Data Structures) he talks about fundamental differences between objects and data structures. Objects benefit from encapsulation, data structures don't.
There is a section about the Law of Demeter:
Uncle Bob gives an example of a LoD violation:
So this is probably where your co-workers are coming from. I think the argument "we have to do this because LoD" is imprecise at best. The central issue isn't LoD so much as whether the API consists of objects or data structures. It does seem like an unnecessary and error-prone change to push through when there are more pressing things to do.