4 ms·
Ah! I see where my confusion lies. As others have commented, it sounds like you're talking about using a tool that does dependency injection for you; perhaps so
by davidcuddeback 13y ago
Ah! I see where my confusion lies. As others have commented, it sounds like you're talking about using a tool that does dependency injection for you; perhaps something configured in XML. I see how a tool like that would subvert a compiler's type checker.
My mental model for dependency injection is a little different. I think of dependency injection as a technique instead of a tool. The only tool I used to do DI is a text editor.
Here's an example of how I would use dependency injection (manually) in Java:
interface Dependency {
void doSomething();
}
class Foo {
Foo(Dependency x) { ... }
}
new Foo(new ConcreteDependencyA());
new Foo(new ConcreteDependencyB());
Both instances of Foo receive their dependencies at runtime, but the compiler is going to check that (1) the concrete dependencies are subtypes of the abstract dependency, (2) the concrete dependencies don't throw extra checked exceptions in their doSomething() method, and (3) the dependent class (Foo) only uses the interface of the abstract dependency. I gain the advantages of dependency injection without undermining the language's type safety.
- namdnay 13y agoI don't think this can be called DI, because you're not injecting them, you're passing them to the constructor. The problem with this model is that you're polluting your constructors with non-functional arguments (for example the logging provider), and that if you want to change one of these, you have to change all your constructor calls!
- davidcuddeback 13y ago> I don't think this can be called DI Sure it can: http://en.wikipedia.org/wiki/Dependency_injection#Manually_injected_dependency http://en.wikipedia.org/wiki/Dependency_injection#Manually_i... http://www.martinfowler.com/articles/injection.html#ConstructorInjectionWithPicocontainer http://www.martinfowler.com/articles/injection.html#Construc... > because you're not injecting them, you're passing them to the constructor Injection simply means that the dependency is sent from outside the class. That's exactly what's happening in the constructor example. > The problem with this model is that you're polluting your constructors with non-functional arguments I don't consider it "pollution." Who says that IDependency is non-functional? I don't think I've ever injected a non-functional dependency. > if you want to change one of these, you have to change all your constructor calls! This is a red herring. If you have to change all your calls, you failed to make your code DRY. That's the programmer's fault. Use a factory or default arguments. 95% of the time, the reason for injecting a dependency is that you want to use a fake implementation in your unit tests, but a particular concrete example in your production code. Have the default constructor setup the concrete dependency and use the extra constructor for your unit tests. This works most of the time for me. interface IDependency { void doSomething(); } class Foo { Foo(IDependency) { ... } // for unit tests Foo() { this(new ConcreteDependency()); } // for production } Doing DI manually requires a little bit of skill, but not much. I actually find that it teaches design principles more than it requires apriori knowledge of them. Having never used a configuration-based DI tool, it wouldn't be fair for me to conclude with any comparison between the approaches. However, given that this conversation started when you complained that DI tools subvert a static type system, I'm inclined to believe that the DI tools introduce accidental complexities that outweigh their benefits in the simplest use cases.
- namdnay 13y ago> I don't think I've ever injected a non-functional dependency Logging is the classic example. Or the data service provider, or CXF-type stuff > Use a factory or default arguments I think that's the main point. Framework-managed DI puts a stop to the factory madness that you always ended up with back in the old Java or C++ world. > Have the default constructor setup the concrete dependency and use the extra constructor for your unit tests This will work for a while, but when you start needing to test complex flows, you're going to end up having classes calling classes calling classes, having multiple constructors will become unmanageable. > DI tools introduce accidental complexities that outweigh their benefits in the simplest use cases I totally, fully, definitely agree with you! The heavy tools such as Spring or EE6 are ugly, frustrating behemoths with absolutely no advantages for small or even medium developments. But as soon as it starts becoming a 40MY+ project I find you can't avoid them :(