Law of Demeter
en.wikipedia.org
en.wikipedia.org
As noted elsewhere, this is not really a "law" - it's a design guideline that many find (usually) helpful. However, it was named a "law" and the name has stuck.
It is a law in the regulatory rather than scientific sense, in that it was adopted as a coding rule in the Demeter project.
Good object should be like like stereotypical reserved introvert Finn: respect the privacy of others, don't talk to strangers, keep only few close friends.
Who? Huck Finn? or a cartoon character?
C2 page: http://wiki.c2.com/?LawOfDemeter
This however adds complication to constructors in OOP and is what I assume has always been the reason for the rise of Dependency Injection.
https://medium.com/@CDehning/when-to-use-fakes-instead-of-mo...
Code that demands more communication then what people are comfortable with is bound to fail (become unmaintainable and produce many bugs), so the law of demeter codifies communication structures as a law about code dependencies and architecture.
It follows that for small teams and personal projects the law of Demeter might not be worth following since they don't suffer from these communication problems. Also microservices an application of the law of Demeter to larger chunks of code instead of just classes.
In object oriented programming classes are basically black boxes of code with well defined interfaces. In that sense modules, packages and microservices are just new types of "classes" we invented since the law of Demeter was formulated in '87. The purpose of all of those constructs is just to give a clear interface to a black box of code, just that those black boxes get successively larger.
(I'm probably rehashing some trivial objection, but throughout all my years doing OOP, that one "law" never made much sense to me.)
If you do lots of refactoring, it's great. It could well be the case that most people don't refactor as fast as people did with the Refactoring Browser in Smalltalk. No refactoring was enabled, unless it was guaranteed to be correct, and you could fire them off almost as fast as you could select the right-click menu. On top of that, you had unlimited undo/redo on the refactorings, and on top of that, there was always the Smalltalk Change Log as an ultimate safety net. So when I was in the zone, I would sometimes get an idea, fire off the prototype of a major refactoring as a dozen rapid-fire operations, look at the result, then back it out if I didn't like it.
Refactoring was more exploratory in those days. The Law of Demeter pre-dates those Refactoring Browser days. Basically, it dates from a time before the Refactoring Browser, when people wished they could refactor that fast, but still used the practice heavily.
If ease of refactoring isn't that high a priority to you, the cost/benefit of warping your practices around refactoring might not pan out. "Simple as that," to quote the recently departed.
The point is to avoid making what should be transitive dependencies into direct dependencies. The reason for this is that you may terminate transitive dependencies with interfaces, but only if you don't write train wrecks like a.getB().getC().doTheThing(). You should, compose the interfaces so that you are expressing a.doTheThing().
But sometimes ya gotta do what ya gotta do, which is why this was later comically referred to as "The Jolly Good Principle of Demeter" instead of "Law".
Or as Jesus said: The Law of Demeter was made for man, not man for the Law of Demeter (Mark 2:27).
a.d() <=> a.b().c().d()
Which call should you make?Obviously a.d(), not only because it is shorter but because it makes your code independent of the fact whether a.b() returns something that understands c() which returns d() whose result is what you want.
a.d() is better code then than a.b().c().d() because it makes fewer assumptions about the rest of the system.
Now assume such a.d() does NOT exist. Would it make sense to create it? Yes because THEN you will be able to write better code by more simply writing a.d() .
Now there is a cost to writing a new method a.d(). But typically that cost is not much because its implementation is simple "return this.b().c().d()" .
But then you realize b().c().d() should be replaced by b.d() to follow the Law of Demeter again. So you actually must pay the price of writing two new methods if you want to follow Demeter. Is it worth it? It depends on how much you value maintainablity vs. having to write less code right now.
My guess is that following Demeter is almost always better.
I prefer a more relaxed form of the law: you can do something like a.b().c(), where b is a method that returns an interface. I think of it as a "composite interface". You can repeat the nesting as much as you like, however it gets a little hard to read after a while.
When I'm thinking about an algorithm for solving a problem I sometimes need extra info from many other places. I sometimes need to use globals to get around all this message passing nonsense that would otherwise clutter my function calls with 50 parameters.
I'd use GoTos if they were better supported.
I don't usually program for others, and I don't call myself a good programmer though. I usually just need to solve my own problems, then move on to the next.
If your function call needs 50 parameters (I assume this is hyperbole) then you need some encapsulation. Many codebases with complex functions actually create a struct for the function call, which actually adds forwards compatibility if new struct members are optional.
If you use globals to pass state then you’re probably not thread safe and you’re certainly going to have problems testing because you’ll need to “prep” global state before every test.
There's an object asking to be born!
One example, sometimes DRY'ing up things that just happen to use the same logic now, but don't existentially/necessarily use the same logic. When they stop using the same logic, you add extra 'magic flags', or you've got to undo the architecture.
Sandi Metz says "prefer duplication over the wrong abstraction ". https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstracti... She also writes about how devs are reluctant to change existing abstractions (for both rational and irrational reasons), making the wrong abstraction even more expensive.
Which isn't to say that lots of duplication all over the place is a good sign either, obviously not.
I wouldn't call this over engineering. I call it laziness to learn and refactor.
Exactly my point. :)
The DRY principle was created by the authors of The Pragmatic Programmer, and as they formulated it, your example is not DRY.
DRY is about requirements, not code or logic. The specification of a requirement should exist in only one place in the code. It aims to reduce instances of "The requirement changed, and I thought I fixed all the places in the code, but I forgot one and it became a bug." If two pieces of code have very similar logic/code structure, but are tied to different features, then the DRY principle absolutely does not suggest you should refactor to make that logic appear in only one place.
Pretty much every time I've seen people complaining about DRY, they're complaining about something unrelated to the original DRY principle.
"No true scotsman" is a very popular way to argue about programming on the internet.
But the original question was "what code sins do you see committed in the name of DRY?", right?
I think in practice it can be hard for many people to tell the difference between "the specification of the requirement being in more than one place" and other kinds of apparent code duplication. Because it's not always entirely obvious what "the specification of the requirement" is, or what it looks like implemented in code.
Because it's so easy to do DRY "wrong", or to think you know what it means but be incorrect (in general or a specific situation), is exactly why one should be careful about it, and just because something appears to you (or a colleague) to be about "DRYing up the code" doesn't necessarily mean it's a good idea.
It doesn't mean there's nothing of value in the principle, just that one should be careful with it.
There are few principles of software engineering that don't have some truth, and also few that aren't misused or abused.
Certainly that particular example of "things that just happen to use the same logic now, but don't existentially/necessarily use the same logic" is exactly a description of when not to de-duplicate code, that's why I mentioned it! It is a "sin committed in the name of DRY", one reason to identify it is to hopefully help others avoid it. The point is not that people doing that are getting DRY right, it's exactly that they're getting it wrong, but in the actual world of actual code, it is gotten wrong with some frequency.
If you aren't sure whether two passages of code that seem similar are really sharing a "specification of a requirement in code" or not, those of us who have experienced (and written!) lots of code that errs in acting as if they were, might urge to prefer risking error on the side of acting as if they were not. Or, again, as Sandi Metz says, duplication can be better than the wrong abstraction.
There's a huge difference between OO/scrum/agile and the DRY principle. OO does not have a well defined origin and definition. Agile does, and is intentionally not well defined. Scrum does have an origin, but I bet if you read the original author's description of it, you'll find it also is not well specified. Pick any "No True Scotsman" argument and the root cause of all of them is the lack of a clear definition. That's simply not the case with DRY.
Furthermore, all these three are a lot more complicated than the DRY principle.
The DRY principle has an origin, and a well established meaning. If you go to its Wiki page, it's nice, concise, and precise. There's no real dispute about it. Generally, those accused of misusing it have never even read the definition, and are taking a concept they heard from an Nth degree source and are interpreting it by its name.
There are likely more people in the world who misuse the principles of quantum physics than those who use it and understand it properly. We don't criticize quantum physics for it. It is precise, and is not the cause of all its misuses.
The other problem I have about people criticizing the "overuse" of DRY: Had the DRY principle never been formulated, you would see no less of it. Coupling different requirements via a common code merely because the logic is identical is probably as old as programming itself. I myself was guilty of this (and bitten by it) long before I had heard of the DRY principle. Someone formulating the DRY principle did not exacerbate the problem. People creating these problems are mislabeling them because they've heard of the principle's name. My challenge to you: Whenever someone couples code in the name of DRY, ask them where they learned that they should do this. I doubt any of them will list any reputable book, for example.
Which again contrasts this with OO/scrum/agile, of which there are several books with quite different interpretations, and where one of the key annoyances is that everyone can back their stance using well known books.
Getting DRY right can be quite challenging in some projects - I'm not trying to claim otherwise. In fact, I believe the original authors were not speaking only of code, but also of documentation, databases, etc. One representation that will cover all of the above. It is clearly challenging to take it to that level. I'm all for criticizing it on those grounds. My complaint is that every criticism I've seen of it wasn't about how difficult it is to follow correctly, but about someone coupling two clearly different requirements with common code.
I’m quite attracted to writing “good” code — in that I think about theory and principles a lot — and “DRY” is not part of my vocabulary. I very much agree with your point that over-engineering is a problem but DRY is not related to that.
I get the point of OO and see how it's supposed to be used, and how handy it would be in large systems, but in my world the information hiding has been more of a hindrance than a help. Maybe what I really want to say is that it's harder than people think to do OO well, and when it's not done well, it just make everything more buggy and complicated.
An exercise I often use is to pretend like whatever I'm writing needs to be usable on multiple projects, so it should be able to be a package that anybody could find useful. Then you work backwards from the interface you'd like a tool like this to have, and obfuscate anything other than the interface. Obviously you're not doing this for most business/controller logic, but for most other things it goes a long way towards avoiding the kind of spaghetti code the Law of Demeter seeks to weed out.
I violate my principles, but I violate your laws.