
Indirection Is Not Abstraction - yegor256a
https://www.silasreinagel.com/blog/2018/10/30/indirection-is-not-abstraction/
======
gregmac
> The users of this software would be very disappointed if any of these
> indirections involved adding any number besides 3.

What? No, it works great to add numbers beside 3:

    
    
        class Sample {
            public Sample(IThreeProvider threeProvider) {
                 _threeProvider = threeProvider;
            }
    
            private readonly IThreeProvider _threeProvider;
      
            public int AddThree(int val)
            {
                return val + _threeProvider.Get().AsInt();
            }
        }
    
        public class MyCustomThreeProvider() {
           public int Get() { return 4; }
        }
    

Now you can just do:

    
    
        var sample = new Sample(new MyCustomThreeProvider()); 
        sample.AddThree(2); 
    

And get 6, as you'd expect. ..So long as you read and fully understood the
_entire_ codebase first, anyway.

I think this actually highlights one of the bigger problems in adding
unnecessary indirections: that it practically begs for strange work-around and
hacks and strange layers added on top. It becomes too difficult to change the
underlying classes -- eg, changing the AddThree method to handle an arbitrary
number means also changing the entire concept of ThreeProviders and everywhere
that calls or tests any of the related code -- and as a result it's faster to
workaround.

~~~
JustSomeNobody
Instead of constructor injection, you really should have used an injection
framework for this. Preferably one that uses an xml config file so that I
would have to read and understand THAT also.

~~~
vbsteven
The whole point of having an injection framework is that you should not have
to understand the whole framework or the implementation to use it.

In this case you want a ThreeProvider and you don't care at all how it is
implemented or where the value comes from.

It's good that the dependency on the ThreeProvider interface is defined in the
constructor, that makes it clear that there is a required collaborator and
your class cannot be constructed with an invalid state. If your injection
framework is any good it can use this constructor to do the wiring. For
example in Java Spring the only thing you need to do is add @Autowired to this
constructor.

And on a side note: Spring does not require any XML for DI configuration since
at least 10 years ago.

~~~
zlynx
Great, so now it's magic and no one knows how it works.

The last time I had to work on Java (more than ten years ago) it was easier to
load the app into the debugger than to understand the source code.

That's f'd up.

~~~
vbsteven
And that was 10 years ago. We've come a long long way since then. Nowadays it
would look something like this

    
    
        @Configuration
        public class ThreeConfiguration {
            
            @Bean @Profile("dev")
            public ThreeProvider devThree() {
                return new DevThreeProvider();
            }
            
            @Bean @Profile("production")
            public ThreeProvider prodThree() {
                return new ProdThreeProvider();
            }
            
        }
        
        @Component
        public class Sample {
            private ThreeProvider threeProvider;
            
            @Autowired
            public Sample(ThreeProvider threeProvider) {
                this.threeProvider = threeProvider;
            }
            
            public int addThree(int val) {
                return val + threeProvider.getThree();
            }
        }
     

And that's about it. The Sample class does not need to worry where it
ThreeProvider comes from. And the configuration about all the different
implementations and in which cases they will be used are bundled together in
one class.

~~~
TickleSteve
...and the whole (non-java) software world cringes while the Java guys look
puzzled wondering whats wrong with it.

~~~
vbsteven
Can you explain why this is so cringeworthy to non-java devs?

~~~
JustSomeNobody
1\. There's Magic.

2\. It is derived from the desire to create not applications, but application
frameworks that can handle any conceivable change request that the PO might
come with. This is architecture astronaut territory. It's frustrating to work
with. It's hateful to debug.

~~~
vbsteven
1) where is the magic in the aforementioned code?

2) the ThreeProvider example is contrived indeed. In real projects this is
used to provide different implementations for loggers, or sms gateways or
other things that can differ between environments (prod, dev, test)

And to decouple the construction of objects from their usage. For example a
rest api controller should not care how to instantiate a database connection
and associated code, it should just require an implementation of a Repository
interface for the model it uses.

~~~
culturedsystems
The magic is in all the code that interprets those annotations. So you have to
understand a large and complex framework to understand which objects are being
injected (in Spring, for example, you have to keep in mind the rules about
which beans are instantiated and then which of the instantiated beans are
chosen for injection into a particular class), and to understand what actually
gets injected (is it actually the object returned from the annotated method,
or some proxy or chain of proxies adding additional behaviour to those
objects?).

The principle of dependency injection is a good one, but dependency injection
_frameworks_ are an unnecessary and overcomplicated way of achieving it. XML-
based DI frameworks at least had a rationale - the idea was to externalise
configuration, so you could change the dependencies without changing the code.
It turns out this kind of externalisation isn't actually valuable in most
cases (changing the dependencies is just as complicated and risky as changing
code, and often needs to be coordinated with more substantive code changes, so
the separation is artificial).

Once you determine that you should wire up your dependencies in code, though,
there's no need for dependency injection frameworks - you can just directly
write the code that wires up the dependencies.

~~~
kovrik
Can't see where this 'magic' comes from. IoC frameworks are not rocket
science. Everything is very-well documented and pretty straightforward.

> dependency injection frameworks are an unnecessary and overcomplicated way
> of achieving it.

But why? I don't want to write my own DI framework for each new project.

I'd better add one dependency, put some @Autowired (and other well-documented)
annotations and continue writing business logic.

Aforementioned 'magic' is basically creating a context, scanning all
annotations, registering beans, instantiating them (calling constructors),
then injecting them.

~~~
drdeca
I wouldn't mind having things be created through autowiring and the like, if
it didn't make it harder to make the things explicitly, without autowiring.

Ideally, making something automated should not make it harder to do the same
thing manually.

~~~
lmm
Modern Java does fix that much. If you look at the `Sample` class from the
example, it's a normal class that you can instantiate the normal way (passing
its dependencies into its constructor) and ignore the annotations on, unlike
the bad old days of afterPropertiesSet().

------
bjpbakker
Of course the original post about Indirection vs Abstract was by Zed A. Shaw
[0].

I strongly like to urge authors to give credit where credit is due. IMO this
post should have at least mentioned the original article.

[0] - [https://zedshaw.com/archive/indirection-is-not-
abstraction/](https://zedshaw.com/archive/indirection-is-not-abstraction/)

~~~
fhood
Wow. This is a far better article than the one posted. I didn't have to read
it four times to figure out what the point was.

~~~
skybrian
Mileage varies; I like the new one better.

------
taeric
There is a great margin quote in Concrete Mathematics that overs this general
idea, to me:

    
    
        There are two kinds of generalizations.  
        One is cheap and the other is valuable. It 
        is easy to generalize by diluting a little
        idea with a big terminology. It is much more
        difficult to prepare a refined and condensed 
        extract from several good ingredients.
    

Attributed to George Pòlya.

~~~
User23
In a rare fun example of confirmation bias, after seeing Pòlya's name for the
first time, now I'm seeing it everywhere.

~~~
procedural_love
That's a frequency illusion, also called the Baader-Meinhof Phenomenon, not
confirmation bias.

------
layer8
The use of the word "abstract" in "abstract class" comes from the notion of
abstract data types, introduced by Barbara Liskov and Stephen Zilles in 1974
in their paper "Programming with abstract data types" [0], which starts with a
section "The Meaninng of Abstraction" (after the introduction). A simplified
summary is that this is about having an interface that abstracts from
implementation details:

> We believe that the above concept captures the fundamental properties of
> abstract objects. When a programmer makes use of an abstract data object, he
> is concerned only with the behavior which that object exhibits but not with
> any details of how that behavior is achieved by means of an implementation.

It seems to me that the term "abstract" is justified for abstract classes
whose role is to define an interface. (Note that pre-Java "interface" was not
a language construct, i.e. the Java equivalent of "interface" is an abstract
class in C++.) The article argues that "abstract" should only be used when
"details are left out". However, abstract classes leave out the implementation
(or at least part of the implementation), so they inherently leave out the
respective implementation details.

My point is, it is important to understand where the established terms come
from, and what is meant by "abstract" in the context of abstract types [1].

[0]
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.136...](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.136.3043&rep=rep1&type=pdf)

[1]
[https://en.wikipedia.org/wiki/Abstract_type](https://en.wikipedia.org/wiki/Abstract_type)

------
adrianmonk
> _Languages such as C# and Java have done many a disservice with their
> keywords abstract and interface. There is a common misconception that by
> creating an interface one can make an object abstract and improve the design
> of a system._

This doesn't make any sense. In Java, etc., "abstract" is just a technical
term relating to the type system. It means these are types which do not have a
complete implementation, so instances cannot be created. The word is used as
an antonym of concrete, another technical term which describes types that can
be instantiated.

It doesn't mean any of this stuff about changing the abstraction level or
improving the design of the system. The author seems not to understand that a
word can have two different meanings even within a single subject area.

While I'm griping, if you are looking at the semi-arbitrary choice of
programming language keywords and letting that shape the big picture of how
you view software design, you're going to have a bad time. "It said 'abstract'
so this must be the way of creating abstractions." If that's the type of
reasoning you use, the programming language designers aren't the ones doing
you a disservice; you are.

~~~
antpls
While you are right, I find you a bit rude. If the new generations of
developers cannot easily grasp the vocabulary or past concepts of a language,
why do you think the new generation is disservicing itself? If the new
generation feels disserved by an older language, they will move to, or create,
something that better fits their needs.

You can't ask everyone to learn the history of everything since the beginning
to understand how things were thought initially. Java, the language, is aging.

Maybe the author confused terms or didn't used them in the right context, but
maybe Java is simply a confusing language that accumulated too many
experiments, requires too much expertise or experience to grasp.

------
ergothus
> When there are expected dimensions of software change, it can be worth
> paying the cost to create software with higher flexibility. Flexible
> software is harder to create, more complex, harder to understand, and harder
> to maintain.

I agree with what is being said, but the way it is coming across doesn't work
for me - I had to go back to the first sentence because the "harder to
maintain" confused me: What is maintenance if not making changes? If so,
flexible (less coupled) software is easier to maintain (over time). But the
author's central point feels correct: flexible software is requires more
effort to think of writing in that way.

What definitions of "maintenance" and "complex" is the author using and how
are they different from mine?

------
jxramos
> Things which are more concrete are what enables and empowers the
> abstractions – they make everything work. Without concrete elements,
> software is nothing more than a beautiful shell of uselessness.

That is very profound! I never thought about concrete realities as empowering
the notion of an abstraction, its as if an abstraction is a compression of
reality/concepts.

------
yazaddaruvala
More recently I've started to consider abstraction an optimization of code
readability/understandability.

Following the rules of optimization, it should only be done when absolutely
needed.

1\. Do not abstract.

2\. Do not abstract.

3\. Quantitative measurement of the benefit, then abstract.

This has worked out really well for me over the last 3 years.

~~~
j88439h84
You're saying that adding abstraction increases readability, but that you want
to avoid doing that until step 3?

~~~
User23
I read it as understand what your procedure is doing in a full context before
abstracting.

~~~
dsego
If there is anything worse than no abstraction, it is a wrong or leaky
abstraction.

------
infinity0
Instead of developing Guice, Google should have just rewritten their entire
codebase in Haskell, which actually has meaningful abstractions, as opposed to
the non-abstract indirection crap that is "dependency injection".

Fuck Guice.

