Jump to content

Dependency injection: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
Code illustration using Java: Re-work example to show no-injection, manual-injection, and framework-managed-injection.
Line 14: Line 14:


== Code illustration using Java ==
== Code illustration using Java ==

Using the car/engine example above mentioned, the following Java code shows a typical arrangement '''with no dependency injection applied''':
Using the car/engine example above mentioned, the following Java examples show how coupled dependencies, manually injected dependencies, and framework-injected dependencies are typically staged.

For these examples, assume the Engine interface and a an implied SimpleEngine default implementation.


<source lang="java">
<source lang="java">
public class Car {


public interface Engine {
private Engine engine = new Engine();

public void setFuelValveIntake(int gasPedalPressure);

public int getTorque();

}

public interface Car {

public int onAcceleratorPedalStep(int gasPedalPressure);

}

</source>

===Highly coupled dependency===

The following shows a common arrangement '''with no dependency injection applied''':

<source lang="java">
public class SimpleCar implements Car {

private Engine engine = new SimpleEngine();


/** @returns the car speed */
/** @returns the car speed */
Line 31: Line 56:
}
}


public class MyApplication {
//omitting the Engine class since it isn't relevant to this example

//or its implementation is unknown
public static void main(String[] args) {
Car car = new SimpleCar();
int speed = car.onAcceleratorPedalStep(5);
logWithFormat("The car's speed is %s.", speed);
}

}
</source>
</source>


As shown, the Car class needs to create an instance of an Engine class to calculate its speed based on how much pressure is made on the accelerator pedal. A similar implementation would instantiate the Engine inside the <code>onAcceleratorPedalStep</code> method, but such an approach still forces the Car class to know how to instantiate its own Engine.
As shown, the Car class needs to create an instance of an Engine to calculate its speed based on how much pressure is made on the accelerator pedal. A similar implementation would instantiate the Engine inside the <code>onAcceleratorPedalStep</code> method, but such an approach still forces the Car class to know how to instantiate its own Engine.'


Note that because there each Car has its own engine, it is possible to have Car create its own. In this artificial example, Car ''owns'' the Engine, so this is relatively simple. In other examples where you may have a shared dependency, such as a database access subsystem, no dependent ''owns'' the dependency, rather the dependency is ''loaned.''. In such a case the dependency would outlive the dependent, or be shared by a large number of dependents, so each dependent could not simply create it's own version without resorting to workarounds in the language, such as static methods which bypass the object's [[Encapsulation (computer science)|encapsulation]] boundary.


Now, should this example use dependency injection, a possible implementation would be:
===Manually injected dependency===
Now, should this example use dependency injection, a possible implementation might be:


<source lang="java">
<source lang="java">
public class Car {
public class Car {


private Engine engine;
private final Engine engine;


public void setEngine(Engine engine) {
public void setEngine(Engine engine) {
Line 58: Line 93:


}
}

//omitting the Engine class since it isn't relevant to this example
//or its implementation is unknown


public class CarFactory {
public class CarFactory {


public Car buildCar() {
public Car buildCar() {
Car car = new Car();
Car car = new Car(new SimpleEngine());
}
Engine engine = new Engine();

car.setEngine(engine);
}
return car;

public class MyApplication {

public static void main(String[] args) {
Car car = nnew CarFactory().buildCar();
int speed = car.onAcceleratorPedalStep(5);
logWithFormat("The car's speed is %s.", speed);
}
}


Line 74: Line 113:
</source>
</source>


In the code above, the factory takes responsibility for assembling the car, and ''injects'' the engine into the car. This frees the car from knowing about how to create an engine, though now the CarFactory has to know about the engine's construction. One could create an EngineFactory, but that merely creates dependencies among factories. So the problem remains, but has at least been shifted into factories, or builder objects.
In this example, the CarFactory class represents the provider. It is a simple application of the factory method design pattern that makes it possible for the Car class to not need to know how to get an engine for itself. This is now CarFactory's responsibility.

===Framework-managed dependency injection===

There are several frameworks available to further automate this process, allowing a complete delegation of instantiation and marshalling of such dependencies to a "meta-factory", often called a Container or Injector. An example using such a framework could look like this:

<source lang="java">
// Note the same Car implementation, but there's a Car interface declared elsewhere
public class CarImpl extends Car {

private final Engine engine;

public void setEngine(Engine engine) {
this.engine = engine;
}

/** @returns the car speed */
public int onAcceleratorPedalStep(int gasPedalPressure) {
engine.setFuelValveIntake(gasPedalPressure);
int torque = engine.getTorque();
int speed = ... //math to get the car speed from the engine torque
return speed;
}

}

// Note no factory - just the main application
public class MyApplication {

public static void main(String[] args) {
Container c = new DependencyInjectorContainer();
c.bind(Car.class,SimpleCar.class);
c.bind(Engine.class, SimpleEngine.class);
c.init();
Car car = c.getObject(Car.class); // asks injector/container for a car.
int speed = car.onAcceleratorPedalStep(5);
logWithFormat("The car's speed is %s.", speed);
}

}
</source>

In this example, a Dependency Injection Container is used, and bindings are declared between SimpleCar and Car, then between SimpleEngine and Engine. After initialization, the container, when asked for a car, will note that the type of Car registered is a SimpleCar. It then observes that SimpleCar has a constructor with one argument - an Engine, implying that this SimpleCar must have an Engine to be a valid Car. It therefore attempts to create an Engine, noting that the type of Engine it has registered is a SimpleEngine. It creates this (since SimpleEngine in this example has no further dependencies), and passes that Engine to the SimpleCar, which it then returns.

This pattern can be followed with a small or large number of declared objects and their dependencies. This basic pattern, with enhancements based on different scopes and lifecycles, is typical of most Dependency Injection frameworks.


This example does not show the full potential of dependency injection since the CarFactory is very simple. To make it a little more interesting one could consider designing it to provide a single instance of Engine to all cars it builds. More instantiation strategies could also exist and be used as specified in a CarFactory's configuration file (a typical feature in dependency injection frameworks). Also, note that in real programs there will be many classes that, like Car, will need to use many components. In this case, the provider's capability to inject dependencies will remove a lot of coding effort from the dependents.
This example does not show the full potential of dependency injection since the CarFactory is very simple. In a simple case such as the above, managing the dependencies requires slightly more code than the naive no-injection approach. However, as components get more and more complex, or if shared instances of dependencies are required, preventing ''owned'' dependencies from being created by their dependents, then such a framework can provide minimal code to manage a large number of objects and their creation and dependency fulfillment.


== Benefits and issues ==
== Benefits and issues ==

Revision as of 15:20, 4 August 2009

Dependency injection (DI) in computer programming refers to the process of supplying an external dependency to a software component. It is a specific form of inversion of control where the concern being inverted is the process of obtaining the needed dependency. The term was first coined by Martin Fowler to more clearly describe the mechanism.[1]

Basics

Conventionally, if an object, in order to accomplish a certain task, needs a particular service, it will also be responsible for instantiating and disposing of (removing from memory, closing streams, etc.) the service, therefore making it more complex and hard to maintain. Ideally, this object would not need to manage its services life cycle but just have a reference to an implementation of the said services and invoke its relevant behaviors. Dependency injection is a design pattern that can be applied to provide an object with its dependencies and move the code related to the service life cycle to a more appropriate place.

Such pattern involves at least three elements: a dependent, its dependencies and a provider. The dependent is an object that is expected to accomplish a relevant task in a computer program. In order to do so, it needs the help of other objects (the dependencies) that provide specialized services. The provider is the component that is able to compose the dependent and its dependencies so they are ready to be used, while also managing other aspects of these objects life cycle, such as when and how they are instantiated (cached or not, for example). This provider may be implemented, for example, as a service locator, an abstract factory, a factory method or a more complex structure, like a framework.

As a simple example of the above explanation, we can think of a car as the dependent, the engine as the dependency and a car factory as a provider. A car does not know how to install an engine on itself, but it needs an engine to run. The assembling of an engine on a car is a car factory responsibility.

When the dependency injection technique is used to decouple high-level modules from low-level services, the resulting design guideline is called the dependency inversion principle.

Code illustration using Java

Using the car/engine example above mentioned, the following Java examples show how coupled dependencies, manually injected dependencies, and framework-injected dependencies are typically staged.

For these examples, assume the Engine interface and a an implied SimpleEngine default implementation.

public interface Engine {

    public void setFuelValveIntake(int gasPedalPressure);

    public int getTorque();

}

public interface Car {

    public int onAcceleratorPedalStep(int gasPedalPressure);

}

Highly coupled dependency

The following shows a common arrangement with no dependency injection applied:

public class SimpleCar implements Car {

    private Engine engine = new SimpleEngine();

    /** @returns the car speed */
    public int onAcceleratorPedalStep(int gasPedalPressure) {
        engine.setFuelValveIntake(gasPedalPressure);
        int torque = engine.getTorque();
        int speed = ... //math to get the car speed from the engine torque
        return speed;
    }

}

public class MyApplication {

    public static void main(String[] args) {
        Car car = new SimpleCar();
        int speed = car.onAcceleratorPedalStep(5);
        logWithFormat("The car's speed is %s.", speed);
    }

}

As shown, the Car class needs to create an instance of an Engine to calculate its speed based on how much pressure is made on the accelerator pedal. A similar implementation would instantiate the Engine inside the onAcceleratorPedalStep method, but such an approach still forces the Car class to know how to instantiate its own Engine.'

Note that because there each Car has its own engine, it is possible to have Car create its own. In this artificial example, Car owns the Engine, so this is relatively simple. In other examples where you may have a shared dependency, such as a database access subsystem, no dependent owns the dependency, rather the dependency is loaned.. In such a case the dependency would outlive the dependent, or be shared by a large number of dependents, so each dependent could not simply create it's own version without resorting to workarounds in the language, such as static methods which bypass the object's encapsulation boundary.

Manually injected dependency

Now, should this example use dependency injection, a possible implementation might be:

public class Car {

    private final Engine engine;

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    /** @returns the car speed */
    public int onAcceleratorPedalStep(int gasPedalPressure) {
        engine.setFuelValveIntake(gasPedalPressure);
        int torque = engine.getTorque();
        int speed = ... //math to get the car speed from the engine torque
        return speed;
    }

}

public class CarFactory {

    public Car buildCar() {
        Car car = new Car(new SimpleEngine());
    }

}

public class MyApplication {

    public static void main(String[] args) {
        Car car = nnew CarFactory().buildCar();
        int speed = car.onAcceleratorPedalStep(5);
        logWithFormat("The car's speed is %s.", speed);
    }

}

In the code above, the factory takes responsibility for assembling the car, and injects the engine into the car. This frees the car from knowing about how to create an engine, though now the CarFactory has to know about the engine's construction. One could create an EngineFactory, but that merely creates dependencies among factories. So the problem remains, but has at least been shifted into factories, or builder objects.

Framework-managed dependency injection

There are several frameworks available to further automate this process, allowing a complete delegation of instantiation and marshalling of such dependencies to a "meta-factory", often called a Container or Injector. An example using such a framework could look like this:

// Note the same Car implementation, but there's a Car interface declared elsewhere
public class CarImpl extends Car {

    private final Engine engine;

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    /** @returns the car speed */
    public int onAcceleratorPedalStep(int gasPedalPressure) {
        engine.setFuelValveIntake(gasPedalPressure);
        int torque = engine.getTorque();
        int speed = ... //math to get the car speed from the engine torque
        return speed;
    }

}

// Note no factory - just the main application
public class MyApplication {

    public static void main(String[] args) {
        Container c = new DependencyInjectorContainer();
        c.bind(Car.class,SimpleCar.class);
        c.bind(Engine.class, SimpleEngine.class);
        c.init();
        Car car = c.getObject(Car.class);  // asks injector/container for a car.
        int speed = car.onAcceleratorPedalStep(5);
        logWithFormat("The car's speed is %s.", speed);
    }

}

In this example, a Dependency Injection Container is used, and bindings are declared between SimpleCar and Car, then between SimpleEngine and Engine. After initialization, the container, when asked for a car, will note that the type of Car registered is a SimpleCar. It then observes that SimpleCar has a constructor with one argument - an Engine, implying that this SimpleCar must have an Engine to be a valid Car. It therefore attempts to create an Engine, noting that the type of Engine it has registered is a SimpleEngine. It creates this (since SimpleEngine in this example has no further dependencies), and passes that Engine to the SimpleCar, which it then returns.

This pattern can be followed with a small or large number of declared objects and their dependencies. This basic pattern, with enhancements based on different scopes and lifecycles, is typical of most Dependency Injection frameworks.

This example does not show the full potential of dependency injection since the CarFactory is very simple. In a simple case such as the above, managing the dependencies requires slightly more code than the naive no-injection approach. However, as components get more and more complex, or if shared instances of dependencies are required, preventing owned dependencies from being created by their dependents, then such a framework can provide minimal code to manage a large number of objects and their creation and dependency fulfillment.

Benefits and issues

One important benefit of using dependency injection approach is the reduction of boilerplate code in the application objects since all work to initialize or setup dependencies will be made in the provider component[2].

Also, it offers more flexibility because it becomes easier to create alternative implementations of a given service type, and then to specify which implementation is to be used via a configuration file, without any change to the objects that use the service. This is especially useful in unit testing, because it is easy to inject a fake implementation of a service into the object being tested.

On the other hand, excessive use of dependency injection can make applications more complex and harder to maintain: in order to understand the application's behaviour the developer needs to look at the configuration as well as the code, and the configuration is "invisible" to IDE-supported reference analysis and refactoring unless the IDE specifically supports the dependency injection framework. Frameworks such as the Grok web framework introspect the code and use convention over configuration as an alternative form of deducing configuration information. For example, if a Model and View class were in the same module, then an instance of the View will be created with the appropriate Model instance passed into the constructor.

Types

Fowler identifies three ways in which an object can get a reference to an external module, according to the pattern used to provide the dependency:[3]

  • Type 1 or interface injection, in which the exported module provides an interface that its users must implement in order to get the dependencies at runtime.
  • Type 2 or setter injection, in which the dependent module exposes a setter method which the framework uses to inject the dependency.
  • Type 3 or constructor injection, in which the dependencies are provided through the class constructor. This is the main form used by PicoContainer, although it also supports setter injection.

It is possible for other frameworks to have other types of injection, beyond those presented above.[4]

Existing frameworks

Dependency injection frameworks exist for a number of platforms and languages, as can be seen in the following table:

Language/platform DI Framework
ActionScript Spring ActionScript
ActionScript di-as3
ActionScript Syringe
ActionScript lowRa (AS3)
ActionScript Pixlib (AS2)
ActionScript VEGAS and AndromedA this IOC extension.(AS3/AS2/SSAS) (see Introduction IOC based ECMAScript notation)
ActionScript Parsley (AS3) as part of the Spicefactory
C++ Autumn Framework
C++ PocoCapsule/C++ IOC and DSM Framework
C++ QtIOCContainer
C++ C++ Builder coupling dependency injection and component based assembly
ColdFusion ColdSpring Framework
Delphi Delphi Pascal coupling dependency injection and component based assembly
Flex Spring ActionScript
Flex Flicc
Flex Mate
Flex Swiz
Java Butterfly Container
Java Essence Java Configuration File
Java Apache Felix iPOJO
Java Google Guice
Java HiveMind
Java JBoss Microcontainer
Java PicoContainer
Java Openxava
Java JBuilder coupling dependency injection and component based assembly
Java simject
Java Seasar
Java Spring Framework
Java J2EE 5 / EJB 3
Java Naked Objects
Java miocc - Microscopic Inversion of Control Container
Java Spring ME
Java Yan
Java JSR-330 Dependency Injection for Java
Java Tapestry IoC
Java 2 Micro Edition Israfil micro container (CLDC 1.1)
Java 2 Micro Edition Spring ME
JavaScript Squirrel IoC
JavaScript ContainerJS
Microsoft .NET Managed Extensibility Framework
Microsoft .NET Autofac
Microsoft .NET Castle MicroKernel/Windsor
Microsoft .NET ObjectBuilder
Microsoft .NET PicoContainer.NET
Microsoft .NET Puzzle.NFactory
Microsoft .NET Spring.NET
Microsoft .NET StructureMap
Microsoft .NET Ninject
Microsoft .NET Unity
Microsoft .NET The LinFu Framework
Microsoft .NET Ninject
Microsoft .NET NauckIT.MicroKernel
Microsoft .NET WINTER4NET
PHP 4 drip
PHP 5 FLOW3
PHP 5 Crafty
PHP 5 Stubbles
PHP 5 Twittee
PHP 5 Sphicy
PHP 5 Phemto
PHP 5 DiContainer
PHP 5 Garden
PHP 5 Xyster Framework
PHP 5 Lion Framework
PHP 5 Spiral Di Container
PHP 5 Symfony Dependency Injection Container
Perl The IOC Module
Perl Bread::Board
Python Zope Component Architecture
Python Spring Python
Python PyContainer
Ruby Copland
Ruby Needle

See also

Further reading

References