My Authors
Read all threads
Today, another small 2nd-order refactoring. I call it "wrap a Demeter". As with the others, this is a very modest step, but still quite useful in some situations.
Demeter violations are places where a client accesses not just an upstream service, but that service's upstream services, or even that service's service's upstream services. It's a common problem in evolving code, and left untended, a classic source of unchangeable code.
Demeter calls look this:

a.getB().getC().doSomething()

a is getting a B, but is using that B expressly to get a C, then finally calling some method on the C to do something.

(The getX()'s could as easily be exposed fields rather than methods: depends on idiom & language.)
I want to stave off some potential confusion. 1) You might be used to thinking that any chain of '.'s like these is just "fluent". 2) You might, alternatively, be theinking that any chain of '.'s like these is inherently evil. Neither of these assumptions is necessarily valid.
First, "fluent". It might be, or it might not be. Fluency is a really confusing topic, but maybe it help to consider the two most common uses for it: in builders and in streaming chains.
In a builder, the return value for each of our methods between the .'s is the same. If our example could as easily be written a.getC().getB().doSomething(), that would be far more like the builder situation: the ordering of the operations is irrelevant.
In a stream-chain, otoh, the fluency is quite different, in that many/most of the methods actually coerce the type they're called on. Stream operations like map or flatMap do this.

I'm going to upset you now: that's usually a classic Demeter chain under fancy new terms.
Because stream-chains are so exciting for people, I am seeing a lot of them these days. Many that I see are both primitive obsession with a vengeance and Demeter violation with a vengeance.
The good news? Everything I'm saying about wrapping Demeter violations can be done equally as well on stream-chains as on the example I'm using as our base.
Second possible confusion: are all friend-of-a-friend calls inherently evil? Naww. If my Single Responsibility is precisely to introduce you to my friend, or to actually allow strangers to navigate an object tree, well, what can I say: that's my job, not a crime.
I will offer you an alternative way to understand it, tho: if my job is to let strangers navigate an object graph, then that should be my only job, and all the rest of the things we might do with such a graph, like making it or changing it, might better be done elsewhere.
What we'd be saying in that case: my job isn't to reveal to you a concrete object hierarchy, it's to expose an *abstract* object hierarchy while actually blocking visibility to the concrete objects I use to form it.
So anyway, why are Demeter chains a problem? I said they are anti-change. Why? It's simple: that sample chain is absolutely committed to every A having a B and every B having a C. And 1) everyone and 2) every client will be sharing that commitment.
If you have that starting snippet a hundred times repeated in your code, then on the day you realize that some B's don't have C's, but an analogue (D) that can do an analogue to doSomething(), you're going to have to find and change 100 lines of code.
And it can even get worse. Some of your clients don't use *exactly* that snippet, but something ever so slightly different. No regex on earth will find them all.
As often happens, TDD and changeability are closely correlated. Demeter chains can be incredibly difficult to write microtests for. In fact, for noobs to TDD, they are the main initial pain-point.
If I write a client of a, and use that snippet, the *only* way to gain control over the client for testing is to have either a real or fake B and C. If real, I gotta load the B and the C with the right data. If fake, I gotta write mocks returning mocks.
Believe me, after the third time you write mocks returning mocks returning mocks, you'll either see what I mean about demeters being a problem and learn how to wrap them or you'll declare that TDD never works.
So what's Demeter-wrapping? It's the dead simplest thing in the world. In our case, it means adding a doSomething() method to class A and giving it the body that is exactly the same as our snippet. It 's just that easy.
Now, clients of A depend on A. They don't depend on B or C. They just know that A knows how to 'doSomething()'.
Notice, once again, we're not *eliminating* complexity, we're isolating it. This is a huge recurring theme in the second chapter of learning to refactor, possibly the most important theme of that chapter.
Demeter-wrapping has an effect I want to start highlighting. (It applies to some of the prior refactorings as well, but I want to raise its profile starting now.) It gives us a new naming-point.
It is extremely common that our friend way down there, C, is more "primitive" -- further from the domain, closer to the metal. That, after all, is why we use a chain to get to it, because we're progressing down levels of abstraction.
We can take advantage of the new naming-point by actually changing the name from 'doSomething()' to, idunno, pick your domain, 'adjustForGrossIncome()', or anything else at the same conceptual level as A.
This is especially useful in stream-chains, where the 11 .-connected methods are usually 100% primitives around stream operation, and say nothing whatsoever about domain intent.
So, with a trivial refactoring, we 1) make clients have less code, 2) make clients easier to scan, 3) make clients easier to test, 4) improve our code's direct representation of the domain.

It's a dead simple step with powerful follow-on effects in terms of changeability.
I moved my rig out on the deck today, and it's a beautiful spring afternoon. Even if I have to move it back inside tonight, it was a win.

I hope you all get a good win today, too. :)
My soundcloud isn't one. Most of my twitter threads become blogs & podcasts. They're free and spam-free. If you like my content, please subscribe now, at geepawhill.org
Missing some Tweet in this thread? You can try to force a refresh.

Enjoying this thread?

Keep Current with GeePaw Hill

Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

Twitter may remove this content at anytime, convert it as a PDF, save and print for later use!

Try unrolling a thread yourself!

how to unroll video

1) Follow Thread Reader App on Twitter so you can easily mention us!

2) Go to a Twitter thread (series of Tweets by the same owner) and mention us with a keyword "unroll" @threadreaderapp unroll

You can practice here first or read more on our help page!

Follow Us on Twitter!

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just three indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3.00/month or $30.00/year) and get exclusive features!

Become Premium

Too expensive? Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal Become our Patreon

Thank you for your support!