The only legitimate dependency injection anti-pattern that I'm aware of is the Service Locator pattern, which is an anti-pattern when a DI framework is used for it.
All of the other so-called DI anti-patterns that I've heard about, here or elsewhere, are just slightly more specific cases of general OO/software design anti-patterns. For instance:
Constructor over-injection is a violation of the Single Responsibility Principle. Too many constructor arguments indicates too many dependencies; too many dependencies indicates that the class is trying to do too much. Usually this error correlates with other code smells, such as unusually long or ambiguous ("manager") class names. Static analysis tools can easily detect excessive afferent/efferent coupling.
Injection of data, as opposed to behaviour, is a subtype of the poltergeist anti-pattern, with the 'geist in this case being the container. If a class needs to be aware of the current date and time, you don't inject a DateTime
, which is data; instead, you inject an abstraction over the system clock (I usually call mine ISystemClock
, although I think there's a more general one in the SystemWrappers project). This is not only correct for DI; it is absolutely essential for testability, so that you can test time-varying functions without needing to actually wait on them.
Declaring every life cycle as Singleton is, to me, a perfect example of cargo cult programming and to a lesser degree the colloquially-named "object cesspool". I've seen more singleton abuse than I care to remember, and very little of it involves DI.
Another common error is implementation-specific interface types (with strange names like IOracleRepository
) done just to be able to register it in the container. This is in and of itself a violation of the Dependency Inversion Principle (just because it's an interface, does not mean it's truly abstract) and often also includes interface bloat which violates the Interface Segregation Principle.
The last error I usually see is the "optional dependency", which they did in NerdDinner. In other words, there is a constructor that accepts dependency injection, but also another constructor that uses a "default" implementation. This also violates the DIP and tends to lead to LSP violations as well, as developers, over time, start making assumptions around the default implementation, and/or start new-ing up instances using the default constructor.
As the old saying goes, you can write FORTRAN in any language. Dependency Injection isn't a silver bullet that will prevent developers from screwing up their dependency management, but it does prevent a number of common errors/anti-patterns:
...and so on.
Obviously you don't want to design a framework to depend on a specific IoC container implementation, like Unity or AutoFac. That is, once again, violating the DIP. But if you find yourself even thinking about doing something like that, then you must have already made several design errors, because Dependency Injection is a general-purpose dependency-management technique and is not tied to the concept of an IoC container.
Anything can construct a dependency tree; maybe it's an IoC container, maybe it's a unit test with a bunch of mocks, maybe it's a test driver supplying dummy data. Your framework shouldn't care, and most frameworks I've seen don't care, but they still make heavy use of dependency injection so that it can be easily integrated into the end user's IoC container of choice.
DI isn't rocket science. Just try to avoid new
and static
except when there's a compelling reason to use them, such as a utility method that has no external dependencies, or a utility class that could not possibly have any purpose outside the framework (interop wrappers and dictionary keys are common examples of this).
Many of the problems with IoC frameworks come up when developers are first learning how to use them, and instead of actually changing the way they handle dependencies and abstractions to fit the IoC model, instead try to manipulate the IoC container to meet the expectations of their old coding style, which would often involve high coupling and low cohesion. Bad code is bad code, whether it uses DI techniques or not.
The right way to do this is to depend on an interface, and then inject an implementation of that interface into Class B.
An interface is just about the thinnest thing that you can depend on -- I liken it to trying to grab a wisp of smoke. Code has to couple to something or else it won't do anything, but coupling to an interface is about as decoupled as you can get, yet interfaces can provide all the functionality that you could want.
So have Class B's constructor take an interface to the class it needs, and have the factory produce that class as an implementer of the interface. Don't depend on the factory, depend on the interface, and have the factory provide an implementation of that factory.
So yes, you will be using dependency injection, but there's nothing wrong with that. Dependency injection -- particularly simple constructor injection -- should be the "normal" way of doing things. Simply put off new-ing things as far back in your app (and as close to the first line of main
) as possible, and hide the new call in a class designed specifically for creating things.
Bottom line: Don't be hesitant to inject dependencies. That should be the normal way of doing things.
Best Answer
When the object itself is responsible for requesting its dependencies, as opposed to accepting them through a constructor, it's hiding some essential information. It's only mildly better than the very tightly-coupled case of using
new
to instantiate its dependencies. It reduces coupling because you can in fact change the dependencies it gets, but it still has a dependency it can't shake: the service locator. That becomes the thing that everything is dependent on.A container that supplies dependencies through constructor arguments gives the most clarity. We see right up front that an object needs both an
AccountRepository
, and aPasswordStrengthEvaluator
. When using a service locator, that information is less immediately apparent. You'd see right away a case where an object has, oh, 17 dependencies, and say to yourself, "Hmm, that seems like a lot. What's going on in there?" Calls to a service locator can be spread around the various methods, and hide behind conditional logic, and you might not realize you have created a "God class" -- one that does everything. Maybe that class could be refactored into 3 smaller classes that are more focused, and hence more testable.Now consider testing. If an object uses a service locator to get its dependencies, your test framework will also need a service locator. In a test, you'll configure the service locator to supply the the dependencies to the object under test -- maybe a
FakeAccountRepository
and aVeryForgivingPasswordStrengthEvaluator
, and then run the test. But that's more work than specifying dependencies in the object's constructor. And your test framework also becomes dependent on the service locator. It's another thing you have to configure in every test, which makes writing tests less attractive.Look up "Serivce Locator is an Anti-Pattern" for Mark Seeman's article about it. If you're in the .Net world, get his book. It's very good.