I understand that the dependency injection is something done by coding and its all done at compile time. And that Dependency injection coding is easily done now by helper tools like Ninject.
However this is at compile time that the dependency injection occurs. Then what does it mean that dependency injection also occurs at Runtime?
This is straight from wiki about Dependency Injection
Dependency injection is a software design pattern that allows the removal of hard-coded dependencies and makes it possible to change them, whether at run-time or compile-time.
I mean I thought about it and came up with the idea that it may refer to multiple interfaces to a single class when a Delegate calls a metho from one of the interfaces, dependency injection decides something like this at runtime, but when I went deeper into it and even discussed it with my boss it sounded like its not that simple as that. And that then, how does the dependency injection makes this decision and assume it the correct one?
Anyone can help me clear out this question?
However this is at compile time that the dependency injection occurs.
The service type is defined at compile time. This is normally in the form of an interface or a (sometimes abstract) base class. The key point here is Liskovs substitution principle
It states that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may be substituted for objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.)
So we know the contract of the type at compile time
Then what does it mean that dependency injection also occurs at Runtime?
When implementing DI, for example, using an Inversion of Control container the implementation type is not known until run time.
Let’s look at a basic example of making a cup of coffee using Simple Injector as the Inversion of Control container and NUnit to test the code.
Firstly we define the cup and our service type:
public class Cup
{
public List<string> ingredients = new List<string>();
}
public interface ICoffeeService
{
Cup GetCoffee();
}
And an implementation of the service:
public class CoffeeService : ICoffeeService
{
public Cup GetCoffee()
{
Cup cup = new Cup();
cup.ingredients.Add("Coffee powder");
cup.ingredients.Add("Hot water");
return cup;
}
}
Our test method configures the container to return the CoffeeService
when asked for an instance of ICoffeeService
[Test]
public void CoffeeService_GetCoffee_ReturnsCupWithCoffeeAndHotWater()
{
// Arrange
var container = new SimpleInjector.Container();
container.Register<ICoffeeService, CoffeeService>();
// Act
var coffeeService = container.GetInstance<ICoffeeService>();
var cup = coffeeService.GetCoffee();
// Assert
Assert.That(cup.ingredients.Count == 2);
Assert.That(cup.ingredients.Contains("Coffee powder"));
Assert.That(cup.ingredients.Contains("Hot water"));
}
We decide we need to always add cream to our coffee but instead of changing CoffeeService
we instead Decorate it with the new feature (Open/Closed principle).
The decorator
public class AddCreamDecorator : ICoffeeService
{
private readonly ICoffeeService decorated;
public AddCreamDecorator(ICoffeeService decorated)
{
this.decorated = decorated;
}
public Cup GetCoffee()
{
Cup cup = this.decorated.GetCoffee();
cup.ingredients.Add("Cream");
return cup;
}
}
And the new test
[Test]
public void DecoratedCoffeeService_GetCoffee_CupWithCoffeeAndHotWaterAndCream()
{
// Arrange
var container = new SimpleInjector.Container();
container.Register<ICoffeeService, CoffeeService>();
container.RegisterDecorator(typeof(ICoffeeService), typeof(AddCreamDecorator));
// Act
var coffeeService = container.GetInstance<ICoffeeService>();
var cup = coffeeService.GetCoffee();
// Assert
Assert.That(cup.ingredients.Count == 3);
Assert.That(cup.ingredients.Contains("Coffee powder"));
Assert.That(cup.ingredients.Contains("Hot water"));
Assert.That(cup.ingredients.Contains("Cream"));
}
The most important point to take from this example is that we code to and compile against the service type ICoffeeService
and the container gives us the configured runtime implementation. In test one we get an instance of CoffeeService
while in test two we get an instance of CoffeeService
that is decorated with an instance of AddCreamDecorator
– in both cases the caller doesn’t care as it get the service it needs.
2
Dependency injection simply means that you pass in one or more interface implementations, typically to the constructor of the class. The class methods then in turn call methods on these interfaces rather than calling out e.g. to a library directly.
When you do this you can defer the decision about exactly what implementation you want to pass in (inject) until run-time where the decision as to which particular implementation should be used might for instance get configured through a configuration file.
3
Your understanding of Dependency Injection is correct and when you do this by coding it will be done at compile time. To have dependencies injected at run time you need Dependency Injection Container (Provider). It basically does the coding for you, you just need to configure it so it can pass the right parameters.
I haven’t used Ninject in a project myself, but I’m familiar with it, so I’ll try to use it for an example.
A dependency that is changed at compile-time would be one that is explicitly configured in code which is run at startup of the program. e.g. for the Ninject docs:
Bind<IWeapon>().To<Sword>();
A dependency that is changed at run-time would be one that is configured to use a provider, rather than a concrete type, e.g.
Bind<IWeapon>().ToProvider(new SwordProvider());
Then, the SwordProvider
class could provide a CreateInstance()
implementation which, say, hits a database to determine which concrete class to supply. Hence, behaviour could be changed at runtime by updating the database. Or whatever.
I’m late to the party, but I noticed that no one else answered the actual question. Here’s a simple example of runtime DI:
First you need an object with a dependency:
public MyRoom(ILightFixture fixture)
{
this.MyLightFixture = fixture ;
}
And then, in your composition root, include a run-time decision:
public void Main()
{
// make runtime decision (e.g. input from user); respond dynamically...
...
if (userChoice == "Classy")
room = new MyRoom(new ClassyChandelier());
else
room = new MyRoom(new SubduedSconce());
}
Simple as that. Now the mood of your room
object is determined dynamically (at runtime).