Johannes Tuchscherer

Dependency Injection

Dependency Injection

by Johannes Tuchscherer

The video discusses Dependency Injection, a software design pattern presented by Johannes Tuchscherer at the Rocky Mountain Ruby 2012 event. The primary idea of dependency injection is that an object does not take what it needs; instead, it receives what it requires, promoting loose coupling and improving testability. Key points include:

  • Definition of Dependency Injection: It is explained simply as an object receiving dependencies rather than creating them, which helps in decoupling services.
  • Example of Service Testing: Using a 'happy service' example, Tuchscherer illustrates how tightly coupled services make testing complicated. By injecting a weather service into the constructor of the happy service, testing becomes straightforward as a mock can easily be provided during tests.
  • Ruby Frameworks for Dependency Injection: Several frameworks like Needle and Mindy are noted, but Tuchscherer expresses disagreement with the notion that Ruby doesn't need dependency injection. He believes there are valid use cases for it.
  • Custom Framework Development: Tuchscherer has created his own dependency injection framework based on the Method Decorator gem. He shows how the 'plus requires' method in his happy service initialization can automatically set up a weather service instance variable and a setter for dynamic changes.
  • Implementation Details: He details how dependencies can be managed, showing the connection with a location service for additional context. This setup enables efficient testing by allowing developers to mock services, ensuring low coupling and enhancing testability.
  • Considerations and Limitations: Some drawbacks, such as potential debugging challenges, are acknowledged. Tuchscherer emphasizes the advantages of using dependency injection, such as enhanced separation of concerns and easier testing, despite some limitations like the need for every class to have an initialize method.

In conclusion, dependency injection in Ruby is a valuable pattern that can enhance code maintainability and testing efficiency, supported by practical examples and a demonstration of a simple framework. Tuchscherer invites viewers to reach out for further discussion or inquiries.

00:00:06.720 So now, first, I assume that most of you really know what dependency injection is. Just a quick refresher: dependency injection, if you boil it down to a very simple sentence, means an object does not take what it needs; it gets what it needs. I know that's not a very scientific or academic definition, but I think it serves the purpose.
00:00:15.519 If you look at my example, take the happy service. The happy service can only tell you if you're happy, not depending on the weather. If it has a weather service, you need to initialize the weather service for the happy service. In this approach, you have the weather service in the constructor for the happy service. However, please don't do that, as it's problematic for testing.
00:00:39.920 When you want to test the happy service in isolation, you would automatically be testing the weather service as well. Those two classes should not be tightly coupled. As we all know, tight coupling is undesirable. One way to solve this issue is to inject the weather service instance into the constructor of the happy service, ensuring that your happy service always has access to it. This makes testing easier, as you can simply create a mock weather service that always returns true or false, allowing you to test your happy service independently from the weather service.
00:01:20.400 Now, dependency injection in Ruby is an interesting topic. There are actually a few frameworks available, such as Needle, which was recently started by a guy at GitHub, and there's Mindy. However, if you search for dependency injection in Ruby, you'll find that most people say you don't need it, as Ruby has some built-in functionality to handle this. Quite honestly, I do not completely agree; I think there is a place for dependency injection.
00:01:47.280 I came up with my own little framework for dependency injection, which is built upon a gem called Method Decorator. You can see how I use this 'plus requires' in the initialize method of my happy service. The 'plus requires' comes from this awesome Method Decorator gem. If you aren't familiar with it, please check it out; just go to GitHub and search for method decorators—it will be the first result. It's created by a guy named Michael Fairly, whom I deeply admire.
00:02:15.599 This little 'plus requires' method is executed before and after you call the constructor for the happy service. This means that when you initialize my happy service, it will automatically get an instance variable called weather service that is set with a weather service. It will also receive a setter for that weather service, so you can change it on the fly. You might then wonder where I obtain my weather service from. I retrieve it from the initialize method of my weather service. I use 'provides' around it, which informs my dependency injection container—totally hidden from you—that this is my weather service.
00:02:49.120 You might also notice that in the weather service, I employ yet another location service. In my signing method, I ask my location service, 'Hey, is it sunny?' If we are in Boulder, the answer is yes; it's sunny. Testing your happy service is now super easy. You can mock out your weather service, create an instance of the happy service, set the weather service instance variable with your mock, and test whatever you need. This approach allows for loose coupling between the two services and enhances testability.
00:03:10.480 There are a few disadvantages to consider. Some people might say it's harder to debug because you may not know where your other service comes from. It might feel like magic that appears out of thin air. While that is a valid point, I believe the advantages outweigh this minor disadvantage. However, there are a few limitations that I am currently working around. For instance, each class needs to have an initialize method, allowing you to implement the required provides around it, and each class must extend the service provider module.
00:03:54.640 And that's it! Here’s my GitHub and my contact information. Please feel free to send me any questions you may have via email. I think we have about 30 seconds left for questions.
00:04:31.320 Sweet.