Dependency Injection - What is It, and How to Use It.
Tags: programming, architecture
Published: 2020-12-28 06:17:03

### What is a Dependency?

In software engineering, there is a complex-sounding technique called **dependency injection** that aims to help organize code modularly by creating objects that *depend* on other objects. The objects that other objects depend on are called **dependencies**. 

The use of dependency injection helps solve the problem known as 'spaghetti code.' If you haven't heard of this term, it refers to software that is 'held' together by bad design and architectural planning, in which *each object is connected to one another*. This makes codebases hard to maintain. To build software that lasts - thoughtful planning and execution are crucial, and dependency injection can help with the process of producing modular code.

### Using Dependencies in Code

Before we dive into injecting dependencies, I will show you a basic example of how to use one. To reiterate, a dependency is an object that other objects depend on in order to operate.

In the following code, you will see a `Colony` class with a `queen` property, an initializer, and a `formColony` method. There is also the `QueenBee` class and the `Bee` protocol.

```
class Colony {
    var queen: Bee

    init() {
        queen = QueenBee()
    }

    func formColony() {
        queen.startMating()
    }
}

class QueenBee: Bee {
    func startMating() {
        print("Begin mating flight.")
    }
}

protocol Bee {
    func startMating()
}
```

When an instance of `Colony` is initialized, the `queen` property is assigned to an instance of the `QueenBee` class. Note that this `queen` property can be anything that is of the `Bee` type. 

The `formColony` method calls the `queen` object's `startMating` method. As you can see, the `QueenBee` class conforms to the `Bee` protocol and will print "Begin mating flight." when the `startMating` method is called.

The dependency in this setup is the `QueenBee` object inside the `Colony` initializer. Since `Colony` directly references `QueenBee` in the initializer, it is considered _tightly coupled_ with the `QueenBee` object. This is not good, because now `Colony`_depends_ on `QueenBee` to function correctly.

The use of dependency injection will help avoid using dependencies as you have seen here.

### Dependency Injection Using Swift

Within the Swift programming language, there are a few different ways to go about dependency injection - **initializer injection**, **property injection**, and the lesser-used **method injection**.

#### Initializer Injection

When using **initializer injection**, you pass the dependency object through to another object via its initializer. The usage of the dependency object (sometimes called a service) is defined within the object it's being passed to (sometimes called a client) - but the actual creation doesn't happen until it's passed through the client's initializer.

To modify the previous code to adapt dependency injection using the **initializer injection** method:

```
class Colony {
    var queen: Bee

    init(queen: Bee) {
        self.queen = queen
    }

    func formColony() {
        queen.startMating()
    }
}

let firstQueen = QueenBee()

let firstColony = Colony(queen: firstQueen)
firstColony.formColony()
```

Now, `Colony` doesn't directly reference the `QueenBee` object in its initializer. Which means the tight coupling problem has been solved, and any object of the `Bee` type can be used with `Colony`. The above code will print "Begin mating flight." 

Note that I mentioned _any_ object that is of the `Bee` type can be passed into the initializer. This is great because you can exchange the type of bee used as the colony's queen. Of course, this wouldn't happen in the real world because bee colonies must have a queen bee - but a good example is changing the type of hive the colony lives in. I've made slight modifications to the code to show this:

```
class Colony {
    var queen: Bee

    init(queen: Bee, hiveType: Hive) {
        self.queen = queen
    }

    func formColony() {
        queen.startMating()
    }
}

let firstQueen = QueenBee()
let topBar = TopBarHive()

let firstColony = Colony(queen: firstQueen, hiveType: topBar)
```

You can now change the type of bee as well as the type of hive used to form this colony. Another way to integrate dependency injection is through the property injection method.

#### Property Injection
**Property injection** is pretty much exactly what it sounds like - you pass the dependency directly through to an object's property. Here is a modified version of the `Colony` class:

```
class Colony {
    var queen: Bee!
    
    func formColony() {
        queen.startMating()
    }
}

let firstQueen = QueenBee()

let firstColony = Colony()
firstColony.queen = firstQueen
firstColony.queen.startMating()
```

The queen is being assigned via a property, and all methods of the queen will operate as expected when called as seen. In other words, this will again print "Begin mating flight."

#### Method Injection

A lesser-used way to integrate dependency injection is by using a setter method. Setter methods are custom methods of an object that use a parameter to set a certain property's value based on what was passed through the parameter. It works somewhat like initializer injection (in that it uses a parameter to give value a property), but you have to call it yourself after the object has been created.

```
class Colony {
    var queen: Bee!
    
    func formColony() {
        queen.startMating()
    }
    
    func setQueenBee(_ queen: Bee) {
        self.queen = queen
    }
}

let firstQueen = QueenBee()

let firstColony = Colony()
firstColony.setQueenBee(firstQueen)
```

Here, the setter method is the `setQueenBee` method within `Colony.` When an object that conforms to the `bee` protocol is passed through to that method's parameter, it will set the `bee` property to the value of the parameter. This is another way of integrating dependency injection, but it's not the most convenient.

### Conclusion

That's it for dependency injection!

It really isn't as scary as it sounds, and after you try it out for yourself, it will become much more instinctive. It's a simple technique that helps developers build better software by making it modular, maintainable, and scalable.

Thanks for reading! If you'd like more programming content, be sure to check out the rest of the [Rusty Nail Software Blog][1]. You can read the original post [here][2].

[1]: https://rustynailsoftware.com/blog
[2]: https://rustynailsoftware.com/blog/what-is-dependency-injection