6 ms·
I would argue that "dependency injection" at its purist is little more than the side-effect of designing with intent to minimise module responsibility. When wr
by benvan 11y ago
I would argue that "dependency injection" at its purist is little more than the side-effect of designing with intent to minimise module responsibility.
When writing a class, as a rule of thumb I'd posit there's a general advantage to outsourcing units of behaviour or complexity to other modules. Those other modules could be constructed by the class itself, but this is likely to make the current class concerned with the construction details of those modules. One of the simplest things to do, for example, is to ask for those modules to be provided to the class on construction.
This sounds like a great idea until somebody comes along and calls it "dependency injection" and a bunch of us lose our minds.
- sparkie 11y agoFowler has a habit of inventing "special terms" for what people have long considered "programming", and people get sucked into some fundamentalism. I don't like how the author of this article appeals to authority when trying to justify the use of a service locator over DI. > So if Martin Fowler says that it is possible to use a service locator instead of DI in unit testing, then who are you to argue otherwise? This argument is the same as "It is possible to use a global variable instead of an argument when unit testing a procedure." That's really what a service locator is - a facade around global variables, unless of course, you use DI to inject the service locator, then you've not really gained anything, but just inserted another layer of indirection. And that brings us back to why we even use techniques which are now labeled "DI" in the first place - they're basically there to avoid the use of globals (and hence, tight coupling). Interfaces are in place to keep implementations decoupled while providing everything necessary for them to interact.
- sago 11y agoI see this argument pop up from time to time and it confuses me. It's almost as if someone read "global variables are evil" and understood it to mean "global data is evil". Singletons aren't global variables. Neither are service locators. Nor databases. Nor screens. Nor keyboards. Global variables are global variables. Unless they all are. And if you're going that way, you have a single main function, somewhere.
- kaens 11y agoWhen people are referring to global variables these days, they would often be more accurate in referring to "global mutable state". Singletons can be this, service locators can be this, databases can be this. This can be bad, notably when assumptions are made about the state without taking into account that you're not the only one that could be changing it. Depending on what's changing, this can lead to very confusing / seemingly unpredictable behavior. Another way to put it is that the nature of the issue with "global variables" is often present in the things you mention.
- Silhouette 11y agoI agree with your basic point, but I'm not sure you even need the mutability requirement to capture one of the underlying issues: you have an implicit dependency on something elsewhere in the system. Even if that something is constant, or at least constant during any particular program run, it still means you can't change the code that sets it up without checking your entire code base for unintended consequences. From this point of view, mutability just makes an existing fundamental problem worse, though it introduces new problems as well.
- sago 11y agoWhy is it implicit? The point is that it is much more explicit. And use of SL doesn't create a dependency that isn't there otherwise. The dependency will be there anyway, because bits of code do depend on one another. The point is, do you want those dependencies to be managed by another piece of code and another piece of configuration? Or is it better to say what you need in the code where you need it? I'm not religious about it, there are times when being implicit is better, but often it is dramatically more complex for no obvious benefit.
- Silhouette 11y agoWhy is it implicit? Because if we have module A depending on module B via some clearly defined interface, but in fact the behaviour of module B also depends on global module C that is set up elsewhere, then B's interface no longer fully describes what A can expect. If C's scope were limited to what's happening within B anyway then this would just be an implementation detail. If C were given to B as some form of explicit dependency by A, then it would be a specified part of the interface. However, if C effectively has global scope by any mechanism then there is now an implicit interface to change B's behaviour that A doesn't know about. Developers reading or maintaining the code for A, C, and anywhere else that can affect C if it's mutable, then need to be aware of what each other are doing, and changes to any of these parts of the code potentially affect any of the others. (I'm not going to address your other point, because I didn't say anything about service locators in the first place.)
- mpalme 11y ago> So if Martin Fowler says that it is possible to use a service locator instead of DI in unit testing, then who are you to argue otherwise? There are good arguments against a service locator - one of them is presented here: http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/ http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Patte... Another argument again the service locator pattern is this: If you ask the locator for a service which has dependencies you have to resolve those dependencies yourself. So you need to know about the specific implemntation of this service interface which defeats the purpse. If you work around that, you end up with something that is pretty close to a DI container.
- _pmf_ 11y agoI understand the backlash against Uncle Bob and a lot of other celebrities, but I think Fowler focuses on real, practical aspects and does not bullshit around. > they're basically there to avoid the use of globals (and hence, tight coupling) One could say that you merely delegate the management of the globals to the DI framework, just as you do with a ServiceLocator.
- capo64 11y agoBut in a DI framework, every object has its own name for its dependencies. With ServiceLocator, every object needs to use the ServiceLocator's names, so they are not encapsulated.
- jblow 11y agoYeah. I only heard about "dependency injection" a few months ago, and my reaction was that my brain just didn't get it, because, like, why are you making a huge deal about such a simple thing? If we made this much of a big deal out of every idea in programming, we would never be able to get anything done. Since then I keep hearing about "Dependency Injection" so my impression is that it's gaining in popularity. But my kneejerk reaction is always that if someone is talking about this subject, they probably are not a very good programmer, just like if someone is talking about how important UML diagrams are. It is maybe a hasty conclusion but that is where my brain goes.
- sk5t 11y agoGood use of DI can help one write a more comprehensible, lighter, more flexible system, where each class is responsible for doing just one or two reasonably-scoped tasks. Unlike, say, an obsession with UML, DI is _not_ the hallmark of a crap-grade programmer. Nor is it all that simple, in the sense that it can transform your way of thinking about runtime configuration and object lifecycle management, and promotes a more flexible mindset. That said, I absolutely despise autowiring and annotation-based DI...
- nulltype 11y agoCould you provide a concrete example of a good use of DI?
- sk5t 11y agoIn Java-land, let's say you have a class that depends on a micro-ORM library (or just a database connection, or whatever); the micro-ORM depends on a database connection, which might be yielded by a datasource, which in turn might be wrapped by a bounded connection pool. DI lets you push all that stuff to configuration rather than wiring it up in code; it reduces drag during development because you can always say, ah, I don't need to think about how or where the micro-ORM boots up, I can tune it later... I can even share the connection pool across five different consumers that are not aware of each other and share no compile-time dependency.