In the previous post, I said that I was going to talk about how to use the Singleton Design Pattern as a simplistic way to introduce a dependency injection container into a project.
IoC containers take a simple, elegant, and useful concept, and make it something you have to study for two days with a 200-page manual.
And there’s a time and a post for where I could digress, but that’s not this post. Instead, there are a few nuances about this idea that I want to clarify before I go any further:
- Dependency Injection Containers are more than just ways to store objects. They handle other additional logic. I’ll cover more about this later in the article.
- I don’t recommend sticking with an implementation of the Singleton Design Pattern for a container (or for very many things, for that matter).
- The purpose of showing this as a strategy is a way to show how you can take a project with a tight deadline, a desire to use software development best practices, and find some practical middle ground.
All of that to say is that what I’m going to show is not what I consider being a best practice for using dependency injection containers.
Instead, it’s a way to “meet in the middle” when it comes to working under pressure for building solutions for others all the while not wanting to sacrifice sound engineering principles.
The Singleton Design Pattern
But before doing that, it’s important to understand, at least at a high-level, the concepts of dependency injection and a dependency injection container and how we can take the concepts outlined in both and combine them into something.
With that said, here are the highlights.
I’ve once defined this using Wikipedia’s definition:
Dependency injection allows a program design to follow the dependency inversion principle. The client delegates to external code (the injector) the responsibility of providing its dependencies.
The client is not allowed to call the injector code. It is the injecting code that constructs the services and calls the client to inject them. This means the client code does not need to know about the injecting code.
This can get much more technical as it deals with interfaces, points of instantiation and more as provided in the link to the comment thread above.
But if I had to narrow down exactly what I’d want you to take away from as it relates to dependency injection, it’s this:
Dependency Injection is about breaking dependencies among objects such that one object doesn’t know anything about a particular instance of an object. Only the methods as provided by an interface.
And if you don’t fully grasp that right now, don’t even sweat it. At least note that dependency injection is about separating the coupling or the tight dependencies that can exist between objects.
Dependency Injection Container
This entire section could go on for some time, but I’m not interested in doing a deep dive on the topic right now. Instead, I want to provide a simple definition, and I want to explain why this is useful as it relates to dependency injection.
First, the simple definition of a dependency injection container is by Richard Miller:
It deals with the instantiation and setup of our dependencies for us and allows us to just specify them in a configuration file.
Now regarding how this works varies from your language, your framework, your implementation, and so on. For example, if you’re looking for a simple DIC for PHP, then perhaps check out PHP-DI.
I only mention this one simply because many of the people reading this blog are WordPress developers who naturally work with PHP. Another one worth checking out is Pimple.
Enough with the definition, though. Why are dependency injection containers useful? As with anything programming related, this can be something that goes on a long academic tangent.
I’m not interested in doing that.
Instead, here’s a simple definition as to why they are useful:
Dependency Injection Containers know how to instantiate objects and it knows how to configure the objects.
Of course, this implies it knows about the arguments an object requires for its constructor. And it makes it possible to retrieve an instance of an object from the container. It’s worth noting the objects within the container aren’t aware of the container.
From there, we can take an object from the container and them pass it into another class (or inject it into that class) when building our project.
Wait, What About the Singleton Design Pattern?
It was my goal to show how to use this Singleton Design Pattern as a simple method for implementing a dependency injection container, but covering the initial groundwork for this material (that is, dependency injection and containers) is more important than jumping too far ahead.
So if you’ve hung with me this far, that’s great. If not, please don’t hesitate to leave a comment. The final article in this series will be shorter, will include more code, and will show a practical approach to the points I listed above.