Within the vast catalog of Design Patterns available to software developers today, one of the most important to consider when designing an enterprise class RIA is the Dependency Injection Pattern.
Dependency Injection, a term originally coined by Martin Fowler in his well known article Inversion of Control Containers and the Dependency Injection Pattern, is a more specific term for what is otherwise known as Inversion of Control or IoC.
Fowler’s assessment of Inversion of Control containers concluded that the name itself – Inversion of Control – was too generic, thus as a result from his discussions with various IoC advocates they settled on the more specific term Dependency Injection, also known as DI for short. The terms Inversion of Control (IoC) and Dependency Injection (DI) are commonly used interchangeably to describe the same underlying design principle of separating configuration from implementation.
There are three basic forms of Dependency Injection, which are generally referred to as type 1 IoC (Interface Injection), type 2 IoC (Setter Injection) and type 3 IoC (Constructor Injection). Before diving into the specifics of how to implement the various forms of DI, I will first discuss what Dependency Injection is on a conceptual level as well as what each specific form means. The examples outlined here are in ActionScript 3, however it is important to keep in mind that like most Design Patterns Dependency Injection applies to any language which supports an Object Oriented Model.
At the most basic level Dependency Injection can be explained as a way of decoupling classes from their dependencies by injecting the dependencies into them rather than having the classes directly reference specific implementations. A class which directly references other classes is coupled to those classes – these are the dependencies. However a class which does not reference any other classes would probably not be very useful. At some point the dependencies need to be made. Dependency Injection is a solution to how those dependencies are made, and the manner by which they are provided.
For example, consider the following class which illustrates a typical example of a class’s dependency on another class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class ConfigurationManager { //defines the configuration to use private var config:XMLConfiguration; public function ConfigurationManager() { config = new XMLConfiguration(); } public function getLogLevel() : String { return config.getConfig("logLevel"); } } |
From looking at the code above the dependencies are pretty obvious; the ConfigurationManager class is dependent on the XMLConfiguration class. Now this type of dependency is quite typical so at this point you may be asking what is wrong with doing this?
The first problem is that the config property is defined as a concrete implementation:
| private var config:XMLConfiguration |
This violates a fundamental OO principle:
Program to interfaces, not implementations.
More importantly and perhaps pertinent to the topic at hand is that it also isn’t very hard to imagine that at some point we may want to load a configuration from some other means, such as a properties file, a remote service and so on. In order to do so we would need to modify the class, and from this we can deduce that the class does not scale very well.
So we could begin improving our current implementation by simply refactoring the ConfigurationManager class to define the config property as an abstraction, say IConfiguration:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public interface IConfiguration { function getConfig(name:String) : *; } public class ConfigurationManager { //define the configuration as an abstraction private var config:IConfiguration; public function ConfigurationManager() { config = new XMLConfiguration(); } public function getLogLevel() : Array { return config.getConfig("logLevel"); } } |
As you can see this is certainly a step in the right direction, however the underlying problem still remains; we are still instantiating an instance of XMLConfiguration directly in the ConfigurationManager – and that is exactly what Dependency Injection is all about: providing a solution to the recurring problem of managing dependencies between classes, and how those dependencies are provided.
When implementing the Dependency Injection Pattern in an application you do so by creating a context (configuration) which defines all dependencies in an application as well as an Assembler which is responsible for assembling the mappings and associations between objects and their dependencies. This is done by utilizing any combination of the three forms of DI; Interface Injection, Setter Injection and Constructor Injection. Below is a brief description of each form:
Interface Injection
Interface Injection is the process by which all dependencies are injected into an object via an interface. For example, the ConfigurationManager example above could implement an interface which defines the operations needed to inject the appropriate Configuration implementation.
Setter Injection
Setter injection as you may have guessed is the process of injecting dependencies via public setters; both explicit or implicit. Using Setter Injection the ConfigurationManager could provide public setters from which an Assembler could inject the appropriate Configuration implementation.
Constructor Injection
Again as you may have guessed Constructor Injection is the process of injecting dependencies via arguments in the class constructor. Using Constructor Injection the concrete Configuration could just as easily be injected.
Both Constructor and Setter Injection are by far the preferred forms of Dependency Injection. Interface Injection has some major drawbacks as it somewhat leads to convoluted code since multiple additional interfaces need to be defined and implemented. The fact that “special” types need to be created and implemented in order to facilitate DI using Interface Injection greatly limits the potential for its use.
There are numerous frameworks for various platforms which provide out of the box Dependency Injection implementations for all three forms of DI. All of these frameworks handle the wiring necessary for easily implementing Dependency Injection in an application, the most notable being the Spring Framework for Java/J2EE. There are also quite a few DI solutions for Flex and ActionScript applications as well. Optionally you could choose to roll your own however I would first suggest investigating some of the frameworks which are currently available as they more than likely provide what you need. The Prana Framework by Christophe Herreman is a good choice as it is one of the most prevalent DI solution available at the moment for Flex.
Using the ConfgurationManager example from above I have provided a basic example application which demonstrates how to implement Dependency Injection utilizing the Prana framework. The example application uses constructor injection to provide a concrete Configuration to the ConfigurationManager, however I encourage you to experiment with the other mechanisms of injection as well. The example is intentionally kept very simple in that it is only intended to convey the basic concepts of DI and how to use it in Flex with Prana, from this you should have a good understanding of how to implement DI in a larger context.