原文来自ericfeminella.com
Within the vast catalog of Design Patterns available to softwaredevelopers today, one of the most important to consider when designingan 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 concludedthat the name itself - Inversion of Control - was too generic, thus asa result from his discussions with various IoC advocates they settledon the more specific term Dependency Injection, also known as DIfor short. The terms Inversion of Control (IoC) and DependencyInjection (DI) are commonly used interchangeably to describe the sameunderlying design principle of separating configuration fromimplementation.
There are three basic forms of Dependency Injection, which aregenerally referred to as type 1 IoC (Interface Injection), type 2 IoC(Setter Injection) and type 3 IoC (Constructor Injection). Beforediving into the specifics of how to implement the various forms of DI,I will first discuss what Dependency Injection is on a conceptual levelas well as what each specific form means. The examples outlined hereare in ActionScript 3, however it is important to keep in mind thatlike most Design Patterns Dependency Injection applies to any languagewhich supports an Object Oriented Model.
At the most basic level Dependency Injection can be explained as away of decoupling classes from their dependencies by injecting thedependencies into them rather than having the classes directlyreference specific implementations. A class which directly referencesother classes is coupled to those classes - these are the dependencies.However a class which does not reference any other classes wouldprobably not be very useful. At some point the dependencies need to bemade. Dependency Injection is a solution to how those dependencies aremade, 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:
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 XMLConfigurationclass. Now this type of dependency is quite typical so at this pointyou 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 thatit also isn’t very hard to imagine that at some point we may want toload 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 modifythe class, and from this we can deduce that the class does not scalevery well.
So we could begin improving our current implementation by simplyrefactoring the ConfigurationManager class to define the configproperty as an abstraction, say IConfiguration:
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 stillinstantiating an instance of XMLConfiguration directly in theConfigurationManager - and that is exactly what Dependency Injection isall about: providing a solution to the recurring problem of managingdependencies between classes, and how those dependencies are provided.
When implementing the Dependency Injection Pattern in an applicationyou do so by creating a context (configuration) which defines alldependencies in an application as well as an Assembler which isresponsible for assembling the mappings and associations betweenobjects and their dependencies. This is done by utilizing anycombination of the three forms of DI; Interface Injection, SetterInjection and Constructor Injection. Below is a brief description ofeach form:
Interface Injection
Interface Injection is the process by which all dependencies areinjected into an object via an interface. For example, theConfigurationManager example above could implement an interface whichdefines the operations needed to inject the appropriate Configurationimplementation.
Setter Injection
Setter injection as you may have guessed is the process of injectingdependencies via public setters; both explicit or implicit. UsingSetter Injection the ConfigurationManager could provide public settersfrom which an Assembler could inject the appropriate Configurationimplementation.
Constructor Injection
Again as you may have guessed Constructor Injection is the process ofinjecting dependencies via arguments in the class constructor. UsingConstructor Injection the concrete Configuration could just as easilybe injected.
Both Constructor and Setter Injection are by far the preferred formsof Dependency Injection. Interface Injection has some major drawbacksas it somewhat leads to convoluted code since multiple additionalinterfaces need to be defined and implemented. The fact that “special”types need to be created and implemented in order to facilitate DIusing Interface Injection greatly limits the potential for its use.
There are numerous frameworks for various platforms which provideout of the box Dependency Injection implementations for all three formsof DI. All of these frameworks handle the wiring necessary for easilyimplementing Dependency Injection in an application, the most notablebeing the Spring Frameworkfor Java/J2EE. There are also quite a few DI solutions for Flex andActionScript applications as well. Optionally you could choose to rollyour own however I would first suggest investigating some of theframeworks which are currently available as they more than likelyprovide 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 exampleapplication which demonstrates how to implement Dependency Injectionutilizing the Prana framework. The example application uses constructorinjection to provide a concrete Configuration to theConfigurationManager, however I encourage you to experiment with theother mechanisms of injection as well. The example is intentionallykept very simple in that it is only intended to convey the basicconcepts of DI and how to use it in Flex with Prana, from this youshould have a good understanding of how to implement DI in a largercontext.