Back to Basics: Understanding IoC
In my last back to basics post, we talked about dependency inversion and how it is the underlying principle that Inversion of Control or IoC is based upon.
We also talked a little about IoC and the three main forms of control that can be inverted; interface, flow, and creation.
In this post I want to dive a little deeper into IoC as it relates to interfaces and creation, we’ll ignore flow inversion for now, because it is often used in a way that is completely unrelated to the other two.
We’ll focus more on creation since we already covered interface inversion a bit and creation inversion is the “hot thing” right now.
Why would we want to invert interfaces?
We covered much of the reasoning in my post on dependency inversion, but there are some important points that specifically relate to interfaces in programming that I would like to address here.
First we must understand what non-inverted interfaces are.
Let’s take an example. Suppose I have a Kangaroo class that has several methods, among them is Punch. Normally, Kangaroo defines the interface that has to be used if you want to use the Kangaroo class.
By default the constructor for the class and the method signatures all make up an interface that the user of the Kangaroo class must adhere to in order to use the Kangaroo class. The module that contains the Kangaroo class usually also would include the interfaces if one is defined. In this case the interface would be something like IKangaroo.
Why do I say this is normal and not inverted? Because it makes sense that a class would define what it does and how to use it, not some other entity.
An inversion would be if some other class or module told Kangaroo what its interface needed to be. Kangaroo would stop being an independent definer of a service it has to offer and instead become a provider of a service someone else wants.
From our narrow viewpoint of the Kangaroo class we don’t know what kind of interface to provide, so we have to assume it is something like IKangaroo. The way we look at the Kangaroo class can completely change though, if we consider the BoxingMatch class.
If we create a BoxingMatch class, we really want that class to define the interface that all boxers must adhere to. We don’t want to have to have the BoxingMatch class know how to make multiple classes with different interfaces box. So within the module that the BoxingMatch class is defined, we define an interface called IBoxer. Within the BoxingMatch class itself we use IBoxer instead of specific classes that we want to use in our boxing match.
Now we have given Kangaroo a suitable interface to implement. IKangaroo hardly makes sense, but IBoxer gives our Kangaroo a purpose. Our little Kangaroo class is providing a service. He can box!
In this example you can see that we have inverted the control of the interface. Instead of letting the provider of the interface define that interface, we have made the user of the interface define it.
The value we gained here is that we have given control to the higher level module and reduced the complexity of its logic by preventing it from having to deal with every different interface defined by classes that can box.
Up next creation inversion
I’m going to end this post here, since creation inversion is going to be a longer topic, and I am on vacation right now.
In my next post we’ll cover creation inversion and talk about how it relates to Dependency Injection or DI.