Dependency Injection Containers
I got into a discussion on Twitter the other day where I mentioned that I don't like DI. Call it lack of sleep or language barrier (on my part), but I said DI — dependency injection — when I meant the dependency injection container. Having said this, let me explain why I don't like it.
POV
Despite not working for any of the larger PHP joints out there, I get to spend my time with pretty interesting stuff. I wouldn't call it high traffic or big data, but being in the Top 100 websites, we're certainly not the average PHP application out there.
I've shared some numbers perviously, and also in our job posting — don't mean to compare size, but my perspective is just that.
Looking at anything, I'm compelled to ask myself:
- How much time will it save during development?
- How much time will take to educate my team?
- How much time will it eat away in each request?
- How much time will it cost me to rip it out when it's slow?
Yeah, I'm still crazy about technology, but I've also grown up to not just submit to any of it without thorough evaluation. In that respect, I've grown old(er) — but that's (IMHO) healthy sceptism and usually what people call experience.
Hype
At the expense of my own credibility, let me just say (again) that I'm not particulary against new things. My beef is the hype.
The fact is that we use a lot of new technology and I have to admit that it's pretty exciting for me when to try out new things. Trying out doesn't mean that we end up using it, but when we try out we take a close look.
On the newer side of things we ended up using CouchDB, Redis, Membase and ElasticSearch. On one or two of these we have even started hacking and all in all we follow their development closely.
Telling people about what we run, sometimes feels like I run a playground for developers and crazy-about-tech people. It's new and it's bleeding edge, and it may be true to a certain extend, but it also the opposite because we (believe we) found great use-cases for each of these things.
But getting back to patterns, and I believe these prove my point even better than going on about these databases: Be honest to yourself and let's define "new" in this case and in general. Because neither dependency injection nor containers, or NoSQL are exactly new.
I don't want to rain on anyone's parade, but get real — it's been said and done before.
Me, myself and DI
I think the first time I remember someone mention dependency injection was in late 2009 — I saw a talk on DI at the PHP Unconference in Hamburg. I remember sitting in the sessions thinking — "Well, duh. That's what everyone is supposed to do. Good thing someone came up with a catchy name for it!".
I guess that even in 2009, I was pretty late to the game and because I spend most of my time with PHP and related, I figured that Ruby and Python developers probably caught onto the term much earlier. Let alone Java people who live and breath all these catchy patterns.
By that time Symfony (1), had already started their infamous DI container project along with some great documentation to explain what DI (sans container) really is:
Enter Dependency Injection. Instead of creating the SessionStorage object inside the User class, let's inject the SessionStorage object in the User object by passing it as a constructor argument: ... That's Dependency Injection. Nothing more!
(Hope I'm getting some props for linking to Symfony here!)
The good news is that if the Symfony(1) managed to do one thing, they at least tried to make DI popular within PHP. The bad news is that PHP developers are ignorant and don't realize that DI is a re-branded (object-oriented) concept which existed long before Martin Fowler decided to write about it.
Enter 2011
Fast forward two years later and with the wake of Symfony 2, DI containers are the so called new shit. Must have a DI container!
Maybe I'm not getting it, but I think foremost a DI container adds a level of abstraction that is not just not necessary, but also counter-productive. For me a DI container is a registry pattern on steroids, but let's start slow.
Generally, there are two advantages to said registry pattern:
- accessing data in different parts of the application
- storing data for later retrieval
While that makes a registry OK, people end up writing crappy code like this:
function foo() { $db = registry_get('db'); return $db->query("SELECT foo FROM bar"); } var_dump(foo());
... while they should be doing something like this:
function foo($db) { return $db->query("SELECT foo FROM bar"); //... } $db = registry_get('dbConn'); var_dump(foo($db));
(Keep in mind, it's just an example.)
Is the pattern to blame? Or its mis-use? Or the framework enabling it? Or all?
DI container vs. registry
I'm guessing that both patterns can be used and abused.
But while the registry had to be explicitely created and setup, a DI container is configured using a configuration file and creates all these objects on the fly — for you. Which reminds me of another great pattern called singleton.
Make no mistake, but I believe that all design patterns have a right to exist.
But the impression I get is that people mistake them as general solutions.
Problem?
Since most people embrace DI like sliced bread, let me share the obvious drawbacks I see.
Moving away the obvious dependencies from an object makes applications complicated, harder to understand and more difficult to extend.
Code becomes magical (for some people) because creation and initialization of dependencies is abstracted away from them and handled completely seperate somewhere else in the application.
Seperation can also lead to issues being harder to diagnose
Convention over configuration usually clashes with the approach DI containers advocate.
Missing support in IDEs.
Inline code documentation — people don't even get
__call()
etc. right.Seperation of concerns and decoupling of dependencies are goals people advocate but they seem to apply to everything but the DI container (object).
I'm certainly not going to advocate C- over PHP-code, but let me just say: nothing is free.
Fin?
So all in all I understand where you are coming from, but I reserve the right to disagree.
I hope I provided a little more detail as of why I disagree and why I'm certainly not a lost PHP developer.
Getting another perspective!
I had previously written the above paragraphs, but waited to post this blog entry because Lukas Smith offered some inside on all things DI (container).
And after I talked to Lukas during the PHPBBQ / Github meetup this weekend, I'm still not a fan of DI containers but he (and Kai) shared another perspective with me: testing.
A DI container is indeed useful when you need to swap out a dependency in a different environment (e.g. production versus testing) and (hopefully) everything continues to work. I'm trying to remember when I wrote relatively low-level PHP that I needed e.g. to swap out cURL from a class to mock it and I don't remember anything right now.
Abstraction FTW
I think in most cases I rely on some sort of abstraction already. For example, I really dig HTTP_Request2 which via driver pattern allows me to select curl, socket or even mock. I'm not sure if using that via a DI container would be any better (see above).
When I mock, I focus on the code I've written on top of it. I'm not actually testing HTTP_Request2 in that case.
Containers for the lazy?
Lukas also mentioned not having to implement set*()
for all kinds of things because with a container you're able to swap out the dependency somewhere else.
I can see that this might be problem for some people, but I disagree also. For the sake of testable code it's paramount to have these because using a DI container inside your tests is really not appropriate. This might be extreme, but in my opinion using a container defeats the purpose of a unit test — it's supposed to be stand-alone.
Complexity
I just can't see myself using Symfony2 components any time soon. Maybe when it matures and what not, but currently that code-base is a moving target.
I'm inclined to check out Pimple some time though, which seems to be a very lightweight DI container for PHP 5.3. The amount of closures in their examples though, don't exactly make me want to debug anything that comes from it.
Fin
That's all. Thanks for reading this far.
Trackbacks
The author does not allow comments to this entry
Comments