In the previous two posts (available here and here), I talked a bit about the Singleton Design Pattern, dependency injection, and dependency injection containers.

Singleton Design Pattern as a Dependency Injection Container

Not that kind of container (but it’s still a cool shot).

These are all topics that I think are important for developers to know and to understand. If you’ve not read the previous posts, then I recommend it because the code that’s shown later in this post assumes you know a little bit about each of the topics mentioned above.

Furthermore, this is going to be a bit shorter as it relates to the previous two posts. The purpose is simply to show how to use a singleton as a simple dependency injection container.

The Singleton Design Pattern as a Container

Before showing the code for how to use the Singleton Design Pattern as a dependency injection container, I want to make two things very clear:

  1. Using a singleton as a dependency injection container does not replace a true dependency injection container.
  2. True dependency injection containers offer more functionality than what we’re going to see here.

The primary purpose of showing this as a potential strategy is because there are times in which we want to implement best practices.

But we don’t always have a chance to do so because of some external circumstance (the least of which isn’t some of the pressures that come with deadlines, etc.).

Ultimately it’s a matter of pragmatism for, say, the first version of the project. It’s something you can always go back and refactor.

With that said, here’s a look at one way that a singleton can be set up as a dependency injection container.

Since this is based on code that I’ve previously shared, there should be very little that’s new. The primary thing to notice is the introduction of an associative array that we refer to as a registry.

This is the place where objects are stored (and identified by a key). Ultimately, this allows us to grab an instance of the Container and then get an instance of the object (by it’s key) we need when we need it.

A Missing Feature

If you recall from the previous post, a container is supposed to handle a few details that this one does not handle:

It deals with the instantiation and setup of our dependencies for us and allows us to just specify them in a configuration file.

In this case, we have the manually instantiate the object then add it to the Container using the available method. Though, it wouldn’t be too hard to introduce this functionality.

Even still, this is okay – in the short term. It shouldn’t stay this way. But that’s not the purpose of this series of posts.

Pragmatism Over Purity

I know that more experienced developers who have read through this series of posts (or even just one of the posts) are likely to disagree with many of the things I’ve said.

And that’s okay.

I’ve tried to reiterate the purpose of this is to leverage pragmatism over purity. The latter can almost always come later, but the former is something that we can implement as we go along when time is short or constraints are tight.

Regardless, the larger picture of high cohesion and less coupling is another thing we should strive for, and if this helps get us one step closer towards that general goal, then I think it’s a good thing to practice.