Lately I’ve been using a lot of Dependency Injection in a big enterprise application as a way to satisfy different customers needs. Application is being used in couple of different countries and it is tailor made to match specific needs and specific regulation laws. It’s being developed by three dislocated teams and since it’s written in .NET 100% I think it was the right way to go. That got me wondering if the similar setup would be needed in Ruby world.
I’ve noticed a couple of blog posts claiming that you don’t need Dependency Injection since Ruby is so malleable. I don’t fully agree.
I do agree with Jamis Buck that it is generally a bad idea to speculate about where you will need Dependency Injection and to put infrastructure up front for it.
If you are unsure what a Dependency Injection is take a look at the example below:
|
If you follow the Single responsibility principle you’ll extract the code for tax code calculation in different class which is responsible for it:
|
So far, so good. I urge you never to speculate whether you’ll need extendibility. Why? It’s really easy to add later on if the need arises. As Avdi Grimm says in his Objects on Rails
Much like premature optimization, premature change management usually misses the mark
Now let imagine our application is a smashing success and we are selling in Brazil also. They have a bit different algorithm for tax_code:
|
We can panic and add tax_code_brazilian to the Customer:
|
We could add type param and that is probably what I would do if I only had two customers:
|
Dependency Injection pattern helps when methods like the one above begin to get out of hands. This is how you can do it:
|
Basically you just postpone the decision of what tax_code generator is going to be used. It is also beneficial as a way to decouple Customer from TaxCode generators, since Customer doesn’t event know the type of the generator it will use. In real world this also eases up testing in isolation.
Variation on a theme would be setting generator on initialize:
|
You may want some kind of configuration so you don’t need to create factory and/or think about it. I think this would be the easiest way to accomplish that:
|
Let’s develop it some more to make it somewhat more appealing:
|
Why stop there, we can easily use Ruby glorious block syntax:
|
Now I know we are onto something because this is exactly how it’s done all over the various Ruby gems.
Couple of iterations later I’ve ended up with this syntax. I’ve also renamed it to Implementation since it is bound to different implementations, so it kinda makes more sense:
|
This gives me a central place to configure system. It gives me peace of mind not to speculate up front what needs to be extendible and I would argue it makes code more pleasable in case of lots of different implementation variants.
For a conclusion I can say:
We don’t need heavy Dependency Injection framework in Ruby, but we sure need the idea of “inversion of control”. The fact that it is so easy to do is a reason that we don’t need framework (and also the reason why I love Ruby so much).
If you are interested here are the whole 13 lines of code. I apologize for the lack of my Ruby skills and am open for suggestions :)
|
Of course in Ruby there is alternative to this. You can put different implementations for different customers in customer folder and monkey patch the behaviour that needs to be different. I don’t find that approach better than the one listed above.
In my opinion it’s better to be explicit about what is pluggable. Also monkey patching and playing with load paths forces you to develop in customer folder and I think you are better of developing in feature folder. The bigger the project and the bigger the team is the harder it is to know exactly what algorithms you have supported so far. Having them all in one folder named after the feature really helps a lot.
EDIT: After checking my notes with the authority (and my personal hero) on the subject Martin Fowler I would say that I’ve described two different patterns that both in effect decouple from concrete class implementation.
We had two variants of Dependency Injection, first being Setter Injection and the second Constructor Injection (Third is Interface Injection but that doesn’t make much sense in Ruby). Next pattern was Service Locator that was coded as Implementation class. I’ve marked the code snippets accordingly.
I think part of bad reputation that Dependency Injection has in community is because people imagine a lot of overhead and red tape that is needed in let say Java or .NET.
Ruby makes is all so much more natural. We may end up not noticing that we achieved decoupling that was the goal of both Dependency Injection and Service Locator.