Once upon a time I've stumbled upon The Law of Demeter, which says that you can only call methods on:
- self's fields
- current method's parameters
- objects you created
- global objects
It's often shortened to never use two dots in a row. The implication of this law is that if you legitimately obtained
order.customer_nameare valid, but
At first I interpretted this law literally and (just like you did a moment ago) reached the obvious conclussion that the idea is total bollocks - writing (or even worse autogenerating) thousands of proxy methods like
def customer_name; customer.name; endor even
delegate :name, :to => :customeris not going to improve quality of your code at all.
It sounded like thousands of those little silly rules like "Use factories instead of constructors" that Java code monkeys make up all the time so they can think "If only everybody adhered to these rules, Java coding would be just fine" and keep blaming other people for their problems instead of blaming themselves for being so daft and sticking with Java. The same Java monkeys don't even adhere to their rules, and so Java coding isn't fine, but even if they did adhere it wouldn't help as you cannot fix technical problems on cultural level. So I forgot about the whole thing and moved along - another stupid rule for people who are afraid of the code.
Of course if this was how the story ended I wouldn't even bother blogging about it. On the contrary, I had a sudden realization - sure, the Law of Demeter has many obvious counterexamples of perfectly valid code that's illegal according to it, and would be made much worse if it was enforced. But most of them go away once it's subtly reinterpretted. There's a loophole for "objects you created" which doesn't have to mean "object you created by
ClassName.new" - every object that was created at your request and the producer doesn't hold any reference to it any more can be considered to be "created by you" (old objects whose ownership was relinquished to you should also qualify, but this is a very rare case and I'm not going to talk about it any more). Let's call this interpretation "The New Law of Demeter" and apply it to an exaggerated example:
message = hash.keys.sort_by(&:abs).map(&:to_s).join(" ")
Naively interpretting the Law of Demeter it should not only be illegal but sent straight to the Guantanamo Bay - there are 4 explicit and 2 implicit dots in it, all in a single line.
But this is perfectly sensible piece of code. It does something useful and it's definitely far saner than
hash.keys_sorted_stringified_and_joined(&:abs, " ")or any other Demeter transmogrification I can think of.
But why is it really illegal? Every single dot creates a fresh object -
Hash#keysdoes so, and so do
Array#join. The code doesn't violate the New Law of Demeter in any way!
The Law of Demeter is much more permissive when interpretted this way, but I wouldn't stop here yet. I really think
order.customer.nameis perfectly sensible even if
customeris owned by the
order. What counterargument some proponents of the Law make? Mostly that it makes customer's name harder to mock if you ever want to make order return customer name not tied to any genuine customer. But why should
Order#customerbe limited to returning real
Customerobjects? How about making it return read-only snapshots of current customer? This is exactly how
order.customer.XXXis used vast majority of the time. As snapshots don't need any write behaviour, it's very easy to return a snapshot with overridden
#name, and as a customer snapshot is a fresh object you've just created, you are allowed to call any methods on it you want.
So let's pretend that every time you do
order.customeryou really mean
order.customer_snapshot, and code like this becomes legal:
The New Law of Demeter lets you do a lot more than the old, including legalizing almost all sensible view code, but it still delegalizes a lot of dubious code. For example code like this is definitely not legal, as it expects read-write objects, not fresh snapshots:
order.customer.first_name = "Bob"
This is also not legal, as snapshots should be fresh objects and not be auto-updatable:
customer = order.customer
puts customer.balance # item included or not?
but it can easily be transformed into this legal code:
customer = order.customer
puts customer.balance # item definitely included
Reinterpretted this way, the New Law of Demeter is really useful at detecting code smell. Unfortunately it also becomes much more difficult to apply, as you need to look not only at the syntax but also at the sema an object to which the original creator still holds a reference; and does the method affect state or does it have snapshot access semantics and so on. I'd love to see a Demeter lint which would try to check compliance with The New Law of Demeter in a meaningful way.