My Authors
Read all threads
Alright: Round 2 of "Replace Implementation Inheritance".

Let's take a real-life case, from working with gee-kid on some code the last couple of weeks.
The app takes a bunch of data from different places and analyzes it using a complicated multi-step process. There are three different datasets today that the app analyzes, with more to come.
Think of the most naive design: we just have a different analyzer for each dataset. (That's in fact the design-in-place we started with in our session.) This design is effectively "zero reuse". All the analyzer's are entirely unrelated.
When we look at these three separate classes, we notice an impressive amount of commonality between them, and a modest amount of differences. We see that, in particular, the structure of the main algorithm is almost identical.
With a little bit of love, we could typographically alter all three cases of the main algorithm so that they were textually identical, like:

items = getTheItems()
for(item in items) {
doSomething(item)
doDifferentBasedOnDataset(item)
doSOmething(item)
}
The *different* code is in 1) getTheItems() and 2) doDifferentBasedOnDataset(item). Which have the same name in all three classes, but actually have different code.
There are three things we could do: 1) Hell with it, live with having the same text three times. 2) Replace with a template method. 3) Delegate the differences.
We don't like option 1, because it isn't just *that* function that's identical, there are other functions. It amounts to a lot of duplicated code. When we change duplicated code, we have to change it twice, and humans being human, we often screw that up. So let's reject that.
In option 2, we make Analyzer a concrete super-class, and have three sub-classes, one for each dataset. Each subclass overrides getTheItems() and doDifferentBasedOnDataset(item). Our main algorithm, and any other shared parts, live in the superclass Analyzer.
Option 2 is called the template method design pattern. The superclass provides an algorithm "template", and the subclasses fill in the details for some or all of the steps called by that template.
This relies on implementation inheritance (II). But, in the first round, we already described how much we dislike II. It works wonderfully if we're never going to change it and we got the master algorithm right once and for all and the only two changes ever are those two methods.
It works rather less wonderfully under the ever-changing conditions of brownfield work.

Here's the first round text for reference.

threadreaderapp.com/thread/1258005…
So what to do? I recommend option #3: Delegate the differences. Here's what it looks like.
1) Create an interface for the delegate. There's usually a good name for this, but in our case we'll just call it Dataset.

2) Put a field of type Dataset into Analyzer.
3) Give the Dataset the two API's, getItems() and doDifferentBasedOnDataset(item).

4) Add three concrete implementations of Dataset, each with custom bodies for the two methods.
5) Initialize the dataset field in Analyzer with one of the three Dataset implementations.

6) change the calls in Analyzer to let the dataset field do getItems() and doDifferentBasedOnDataset(item).
That is delegation. In our case, there were actually six differences, so our Dataset interface had six methods, and it took about 90 minutes (in Python) to make the change.
So compare and contrast options 2 and 3. They both eliminate the duplication of the main algorithm, so that's a win.
They both started with 3 classes and ended up with 4, but option #3 also adds one interface, with one API per difference in the algorithm.
If you do microtested TDD, option #3 is usually "easier" to test. The reason is because your rig does not need to create an Analyzer to validate that the three datasets do what they're supposed to do. (If you don't do microtest TDD, there's no difference.)
Now, in a simple case like this one, you can imagine us still debating the policy. The differences aren't that major. But, to return to our example of Node from JavaFx, there are 3-5 levels of option #2, with on the order of a 100 eventual heirs.
And the reason I don't use implementation inheritance is because the simple greenfield case almost effortlessly becomes the staggeringly complex brownfield case over time.
Especially when it is operated on by multiple individuals across multiple periods of time with multiple vocabularies and multiple real-world programming needs.
Now, there is a yabbit here: "Yeah, but, option #3 might not be as performant." More design-crime has been committed on the basis of that yabbit than we can easily summarize. I'll give my standard advice on optimization: "profiler or it didn't happen"
We do not reason about performance, we measure it. *If* your painful design is faster *and* you can prove that it is *and* you can prove it's relevant to value, *then* you can use that argument. Otherwise, talk to the hand, cuz the elderly chubby brownfield geek ain't listening.
So, hopefully, round 1 gave you a sense of why I replace implementation inheritance, and round 2 gave you a sense of how to use delegation in your own design. Round 3, not today, will be about how to use in to work with other people's implemenation inheritance.
As always, if you like my content, put a subscription on it. It's free, it's spam-free, it's not broken in tweets, its available as text or audio, and it keeps me making content.

Subscribe now!

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 two 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!