What is Dependency Injection

If you are a developer, chances are very good that you know what Object-Oriented (OO) code is. You might have also heard about OO Design Patterns, things like single responsibilities, decoupled code, and my subject here: Dependency Injection (DI).

OO Design patterns help you write code that is easier to change, and easier to test. Being quite abstract concepts, they apply to many modern programming languages, including Java, Ruby and PHP. Each language has its own way to do things, thus each language implements the concepts in a different way. Some implementations are similar, others not so much. In this article I’ll talk about Dependency Injection with a special focus on web development. I’ll explain what DI is, and I’ll elaborate on Ruby’s implementation. Finally, I’ll compare the Ruby approach with PHP’s view on DI.

 

Can you tell me what DI is already?

Dependency Injection (DI) can be tricky because it is a very simple concept, you might be using it already without even knowing! Because the definition is so generic, DI is one of those things that it’s easier to just look at an example in order to understand them. Let’s work on that; The following code does not use DI:

Our new MyPORO class has several dependencies. According to Practical Object Oriented Design In Ruby, an object depends on another when:

  • It knows the name of another class
  • It knows the name of a method of another class
  • It knows the arguments a method requires
  • It knows the order of those arguments

Now that we know how to identify dependencies, let’s see how many our little PORO (Plain Old Ruby Object) has. Following the rules we can see it has four dependencies. We know the name of the class, two methods and an argument. This can’t be good, ideally we want to avoid dependencies, so that one object is decoupled from the rest.

Here’s when DI comes in. Let’s inject the dependency in the constructor:

By passing the dependency to our object, thus injecting it, we reduced the amount of dependencies from four to two. All we know is that the greeter object has a say_hi method, and it takes one argument.

That’s it, that’s dependency injection! You can also inject dependencies in setter methods instead of the constructor, and this still counts as DI. This simple implementation is shared across multiple programming languages pretty much unchanged.

Dependency Injection Containers

You might have noticed in the example above, we are in fact removing a lot of dependencies, but where do they go? They don’t magically dissapear. Truth is, another object must have that knowledge. The idea of DI containers (DIC) is simple: Use an object who knows everything about other objects and their relations. This object is in charge of all your dependencies. Ask this guy for any service and he’ll know how to instantiate it and give it to you (in the DIC world, they like calling objects “services”).

Some languages — like Ruby, discourage the usage of DI containers. Others, like Java, embrace it. Before diving deeper, let’s see an example of how a DI container might work. I’ve always liked the work of Fabien Potencer (creator of the Symfony PHP framework), and not just his code, his articles on Service Containers really helped me understand that concept.

In that series Fabien explains a lot about DI and gives pretty good examples. Quoting from his writing, we can see a perfect example of a setter dependency injection:

The same code using a DIC looks like this:

As you can see, the responsibility for getting services has been delegated to the container. If you are interested in DICs check out Fabien’s series. They are really good and guide you though a basic implementation of a DIC in PHP.

The first thing I noticed when coming from PHP to Ruby was that they don’t use DI containers. I learned the reason is mostly because Ruby developers embrace dynamic types over static ones. David Heinemeier Hansson (Rails creator) explains in his blog post why Dependency injection is not a virtue in Ruby.

One of the points he emphasizes most on why a DIC is a bad idea in Ruby is that the code is already easy to test, there’s no need to add complexity. Consider David’s example, publish! has a dependency on the Time class, and a second one on the now method. His point is that it’s not a big deal, it’s still easy to test:

The ways in which Ruby can bend makes writing tests very easy. We know it’s easy to test, but is it easy to change?

Writing code that’s easy to change

We said before that the main objective of Object-Oriented Design is writing code that is easy to test and change. DI containers make things easy to change, as every time your dependencies change, you’ll only need to change your code in one place, the DIC. If you had hard-coded dependencies all over the place, you’d have to change your code in many places, which is never a good thing.

How can we write easy to change code without using a DIC? Well, one solution Ruby developers use is by wrapping your dependencies in methods:

The code above still has a dependency on Greeter, but it’s wrapped in the greeter method. If we change the name of the class, thus affecting the class name dependency, we only have to change the class in one place. The same applies for the say_hi method, if we ever change the name or arguments it takes, we’d only have to change our code in one place: In the greet method.

This might not stop you from having to modify your code in many places when something changes, but it makes changes trivial, a fair tradeoff.

Organizing Dependencies

Another thing you need to consider in Ruby is where to put your dependencies. Well-organized dependencies allow you to make even easier changes, as fewer files will need to be changed.

A good rule of thumb is that dependencies should be specified in the object which changes less. Consider this scenario: You are writing an MVC application. Your controller depends on class A. Class A expects to be injected B as a dependency.

In this case, the controller is much more likely to change than both services. It might be a better idea to change the order of the dependencies:

This way the controller doesn’t care about A’s dependencies. Because A is less likely to change, one could say this code is more efficient.

Conclusion

Ruby gives the developer a bit more responsibility; it’s you who decides how to manage your dependencies. In exchange, you get simpler code and more freedom.

To briefly illustrate what I mean, consider Symfony and Rails. Both are awesome frameworks. In Symfony, you have to learn how to use the DIC and update your dependencies file whenever you want to add a new service. Adding a service certainly involves some ceremony, but fetching it will be easy. On the other hand, in Rails you simply add it to your Gemfile and start using the service directly. There’s no learning curve, less ceremony, but you’ll have to manage dependencies on your own.

As everything in life, both have their pros and cons. I’ll just say, when in Rome, do as Romans do. Ruby is quite mature by now and there’s a reason Ruby developers do it this way. Try it and then judge for yourself.

Dependency injection is awesome. It’s so powerful for such a simple concept! As the base of many more-advanced Object-Oriented Design principles and patterns, understanding DI will only do good things for you.

If you are interested in Ruby’s OO design, I highly recommend Practical Object Oriented Design in Ruby. It really helped me think about OO code the Ruby way.

Leave a Reply