Some Thoughts on Dependiencies and Dependency Injection
Controlling, or managing dependencies has been a problem for a long time in all but the most trivial software systems. Factories, Naming Systems have been used for a long time, recently, dependency injection has received a lot of hype.
But what is the essential "pattern" behind dependency injection? What is really essential? Is it the setter operation that passes in the resources? Hardly. In this post I want to elaborate a bit on this topic.
For dependency management, it is essential that a) a module declares explicitly, which resources it needs to access, b) has a way to actually access them, c) an external entity makes those resources available to the module, and d) the module cannot access any other, non-declared resources. And e), you Often, want some flexibility about the resources at confirguration time, e.g. the module only declared the required resource
types whereas the actual instances of those types are configured into the system at startup.
Item a) is important, so that analysis tools can find out which dependencies exist between modules. b) is necessary, otherwise the module cannot work. a) basically requires d), because if a module can have additional dependencies than analyzing the declared resources does not really help you much. And finally, c) is necessary to make a) and b) work, and to enable the distinction between resource types and resources described in e).
So what about the classical dependency injection scheme where you use a setter operation on the module for each resource? The setter serves as a way to advertise the required resources (introspection can be used to find out about the setters and the required resources described by them). The setters also serve as a way to make the resources available to the module, under the control of an external entity that "wires" the module to its resources. Assuming there is no "backdoor", such as a global variable, in the system there is no way for a module to access resources not declared by the setter.
So, dependency injection is a good way to manage dependencies. We knew that before, however. So, are there any other, not-so-hype means of managing dependencies? Or, asked the other way round: Maybe the "old" schemes aren't that bad, after all. Let's evaluate.
A
factory per se does
not implement the requirements outlined above since it is typically application-global and not module-local. Also, the module does not need to declare its required resources, since it can get everything "stored" in the factory by querying it. You have basically the same issues with a
naming service.
Now enter model-driven development. If, for example, you specify your modules in a model and that model also includes the required resources (e.g. using stereotyped UML class diagrams, or whatever), you can generate from that model various strategies to make the resources available to the module implementation. You can generate a base class that has the DI-setters, just as above. You can also generate a custom factory. It will have a getter for each "legal" resource. Such generated, module-specific factories are often called
contexts. You can also let the module implementation access a naming system to access resources, provided that the naming system (or at least, the specific modules' view on it) only contains the "legal" resources. Of course, in these scenarious, you would perform dependency analysis on the model level.
So, what does this tell us? It's always good to look behind the scenes, read: at the patterns, and not be stuck into thinking that "using setters or constructor parameters is the only way to get dependencies under control".