Monday, October 6, 2014

Inversion of Control, and Dependency Injection (IoC + DI)...uhm...what?

I see it all the time: you ask, "do you know what IoC and DI is." You get, "sure, this framework, and that framework, use them, and I've used those frameworks. And DI is something you do every time you create an instance and pass it as an argument. Three common examples are RobotLegs (ActionScript), Angular JS (JavaScript), and Spring (Java)."

"Ok...now without naming a framework, or using an example from one, in fact without referring to technology at all, tell me what IoC and DI are, and why they are generally spoken of in the same sentence."

If it's a phone conversation, I begin to hear the telltale save-me-Google keyboard taps. If it's in person, I see the forehead produce the uncomfortable light sweat. And I'm not talking junior guys and such; I'm talking Master's degrees, PhDs, the whole bit.

So the honest answer almost every time is, "I use frameworks that have these mechanics. But I really have no idea, apart from the usual 'cleaner/easier' stuff, what these mechanisms are or why the framework designers decided to use them."

It's to be expected. I've never--literally not once--found a simple explanation of these concepts that is understandable apart from a specific language or technology, or even apart from technology completely. I have actually heard IoC and DI referred to as "magic" in an online course on Angular with no further explanation. What is the idea of it?

The following is typical of what I've found, these from WikiPedia:
"In software engineeringinversion of control (IoC) describes a design in which custom-written portions of a computer program receive the flow of control from a generic, reusable library. A software architecture with this design inverts control as compared to traditional procedural programming...Inversion of control is used to increase modularity of the program and make it extensible,[1] and has applications in object-oriented programming and other programming paradigms."
 "Dependency injection is a software design pattern that implements inversion of control and allows a program design to follow the dependency inversion principle."
That's just not going to do it. The techno babble is confusing, and increased modularity and so forth (cleaner code, more testable, etc.) aren't good enough reasons; people often respond, "I know how to write modular code and test it, I don't need the overhead of some framework for that."

Thing is, they're correct. You don't. So why bother?

I came up with the following a while back. I've delivered it more than once, and it seems to do the trick.
Say I run a service. It's front-ended by a long desk with a number of employees standing behind it. Sort of like the counter at McDonalds, or an old bank teller counter. Behind these employees are all kinds of filing cabinets, with dozens of drawers. Each drawer is labeled with the name of the part it contains.
It's the job of each employee, to hand out a box with the "part du jour" to any customer that walks up to the counter.  
When employees arrive in the morning, they look at a board in the back room that tells them what the part du jour is; some write it down, some use memory, whatever. Each employee then goes to their station at the counter. When a customer walks up to the counter and asks for a part du jour (whatever it might be that day), an employee goes to the filing cabinets, finds a drawer that contains the correct part, takes one out, places it in a box, then returns to the counter and gives the box to the customer.
Potential shortcomings and failures in this system:
  • What if the part du jour needs to change during the day? 
  • What if one of the drawers in the filing cabinet is poorly labeled? 
  • What if the employee simply misread the board, or forgot and guesses?
  • What if you need to recall and replace all the parts that were handed out that day? 
And who knows what else.

At this point, people generally start to talk about enhancing the Employee.
  • Create a process to notify employees of a change in the part.
  • Create a process by which the employee can ensure they got the right part from the drawer.
  • Create a process that tests the employee's understanding of what was on the board.
  • Create a process such that employees take down the names and addresses of all customers.
Starting to sound like some software projects you may have been involved in?

Unfortunately, none of this will really solve the problem; these "fixes" will in fact aggravate it, because the root cause of the problem is that the employee has too much responsibility, a great deal of which has nothing to do with their main function; customer service. Adding more unrelated responsibility to the employee may get you some short term tactical success, but will fail as a strategy.

To use the parlance of IoC and DI:
The root cause is that the Employee is in Control of Injecting the Dependency into the Box, and shouldn't be.
It translates easily to programming.
  • The Employee is a Class, a View, etc; a thing that is supposed to have a well defined responsibility, and that interacts with Customers that need parts from it. 
  • A Customer is another class, a function, a user interface components that displays data, etc; some element that needs a Dependency Injected from an Employee. 
  • A Dependency is a part the Employee packages and hands out to the Customers that need it. 
So if a given Employee packages and hands out the wrong Dependency, the system will break down at the Customer level somewhere, and you will have to backtrack to find the Employee that gave that Customer the wrong Dependency. Hopefully, this doesn't happen in more than one place or you will have a lot of debugging and fixing to do.

And then, what if one day, you need to issue a recall and replace a given part from every Customer that uses it? Break out the Red Bull.

So on goes the anecdote:
But what if you did this: 
Create a small team, call them "Packing," which is responsible for packing the boxes. They make pre-packaged boxes available to the Customer Service Employees, who in turn give them to the Customers. 
This small team of Packing Employees has a completely separate and specific process for ensuring they put the correct part du jour (Dependency) in the boxes. The Customer Service Employee no longer has the overhead of selection and packing, and can focus entirely on Customer Service. 
In other words, the packing Control has been Inverted away from the Customer Service Employee to a specialized Packing Team. 
With this "framework" methodology of specialized elements interacting to perform a complex task, how many packing errors can a Customer Service employee make? The answer, is...zero. The only mistake they can make is to somehow never give the package to the Customer, which would be a clear failing of the Customer Service process, not Packing.

I get these kind of follow on questions, all good ones:

But...what about the Packing Team? Can't they make a mistake?

Sure they can. But again, they have a specialized process. Their ONLY job is to ensure they are putting the right part in the boxes. It involves inspection, validation, etc. Everything you need done to ensure proper packaging, but shouldn't be making your Customer Service Employees responsible for.

How does this make the process more airtight?

All employee jobs are much more clearly defined. If a Customer didn't a get a box, you talk to Customer Service. If a Customer got the wrong Dependency, you talk to Packing.

But doesn't this mean more overhead? You have to hire more employees, create more processes?

In a way, this is true. But, if you don't break apart these responsibilities, you'll eventually place so much responsibility on Customer Service that they won't be able to keep up with the demand for Dependencies; the system will cease to scale. By breaking the roles apart and implementing more specialized processes as a framework, you make problems easier to find, and can tweak one process without involving the other.

So there you have it: IoC and DI working together to create more stable, easier to fix and maintain, systems. Read the Wikapedia definitions again; hopefully they make more sense.

Of course there's a lot more to it, but this generally gets the conversation going in the right direction, and I usually see the listener start to discuss opportunities to use IoC and DI together.

As always, thanks for visiting.