Let’s say we work at a robot factory and our boss asks us to create a class for a robot. She says that the robot needs a wire, an antenna, and a power supply in order to function, and we can’t change out parts once it’s made.

So we write up a neat little robot class:

Great! This meets all the requirements.

So we bring this to our boss and our boss replies, “Okay, but we need to be able to build all different kinds of robots. Some of our customers like green wires and 5,000 watt power supplies.”

Dang. So back to the drawing board. How can we dynamically create this robot’s parts when it’s being manufactured? Easy! We’ll use an initializer that takes in the custom values for the parts:

Alright, looking good! We run to our boss’s office and pronounce, “Look at this. Now you can make any kind of robot that you want.”

Our boss looks slightly amused as she says, “Good, but we actually just decided to move our parts manufacturing overseas so our robot can’t be creating its own parts.”

Agh! We sulk out of her office and run into a friend who works in the robot testing department. We tell him about the issues that we’re having with this robot class.

“Why don’t you use dependency injection?”, our friend asks.

“Dependsiwhatsit?”, we reply. Our friend quotes James Shore,

“Dependency injection means giving an object its instance variables. Really. That’s it.”

That’s it! We run back to our computer and quickly come up with a solution using dependency injection:

This is called initializer injection. We’re giving our robot its dependencies (instance variables) when we initialize it. This is very simple in theory and in practice.

In our example, it’s actually simpler to use dependency injection. To see how, let’s look at how our instance variables were assigned in our first implementation:

Look at all of that complexity and specificity. And what did it get us? One kind of robot. Let’s look at how our instance variables were assigned in our second implementation:

Better, but look at what is still happening — our robot knows how to create its own parts. This means that our robot depends on the implementation of Wire, Antenna and PowerSupply. If the initializer for any one of those types changed, we would end up having to also change the implementation of robot’s initializer. That doesn’t seem right, does it?

Now let’s look at how our instance variables are set when using dependency injection:

It’s simple.

Now we might be asking, “Where’s the implementation for Wire, Antenna, and PowerSupply? How do we know this is really so simple without knowing how these other components are created?”

This is the greatest part about dependency injection: we don’t care. We don’t care how Wire, Antenna, PowerSupply, or any other dependency of robot is created. We’re robot engineers; our only job is to create a robot class. All we care about is that we get our parts when we need them (at initialization time).

Let’s point out some of the big advantages of dependency injection:

Transparency

The responsibilities and requirements (dependencies) of an object become much more clear. By seeing what our objects need to function, and only what our objects need to function, we can more easily assume what our objects do.

Segregation

We want our objects to only know what they absolutely need to know in order to function. Did our robot need to know how to create its parts in order to function? As we saw using dependency injection, no it did not. Even though our robot uses its parts, it’s not responsible for creating those parts.

Decoupling

When pieces of our software are coupled, they depend on eachother in order to function. Tight coupling kills reusability and maintainability. When we combine dependency injection with the use of Swift’s protocols, which we’ll see later in this series, we can achieve reduced coupling between our components. By having our objects depend on the interfaces of other components, not their implementations, we will be able to make code changes much faster when architecture or requirements change.

Testability

This is arguably the most important reason to use dependency injection when writing software. The ability to test your code is directly related to how your components get their dependencies. As we’ll soon see, if your objects are creating their own dependencies it can be extremely difficult to control their behavior, which means it can be extremely difficult or impossible to test their behavior.