(Inversion of Control)
What purpose does DI serve?
DI isn’t a goal in itself; rather, it’s a means to an end
One aspect of that is to write maintainable code
DI is nothing more than a technique that enables loose coupling
loose coupling makes code more maintainable
Successful software must be able to change
There are at least four common myths about DI:
DI is only relevant for late binding
DI is only relevant for unit testing
DI is a sort of Abstract Factory on steroids
DI requires a DI CONTAINER
Benefit | Description | When is it valuable? |
---|---|---|
Late binding | Services can be swapped with other services. |
Valuable in standard software, but perhaps less so in enterprise applications where the runtime environment tends to be well-defined |
Extensibility | Code can be extended and reused in ways not explicitly planned for. |
Always valuable |
Parallel development |
Code can be developed in parallel. | Valuable in large, complex applications; not so much in small, simple applications |
Maintainability | Classes with clearly defined responsibilities are easier to maintain. |
Always valuable |
Testability | Classes can be unit tested. | Only valuable if you unit test (which you really, really should) |
Okay, so you can swap out your relational data access component for something
else.
For what?
Is there any alternative to relational databases?
This has changed significantly in the last years.
There are alternatives to relational databases
The replacement argument has now become much stronger.
REAL LIFE EXAMPLE
public static void Main()
{
IMessageWriter writer = new ConsoleMessageWriter();
var salutation = new Salutation(writer);
salutation.Exclaim();
}
Hello World application done in DI style
Looks complicated
But the benefits it provides are much more valuable
Create concrete writer (Console)
Inject it as dependency
Call the action
public class Salutation
{
private readonly IMessageWriter writer;
public Salutation(IMessageWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
this.writer = writer;
}
public void Exclaim()
{
this.writer.Write("Hello DI!");
}
}
Use dependency
Inject it as dependency
Define the abstraction (interface)
It could have other methods as well
Required method
public interface IMessageWriter
{
void Write(string message);
}
public class ConsoleMessageWriter : IMessageWriter
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
Create the concrete class
Which implements the interface (Console)
It could be FileMessageWriter
Concrete implementation
var svc = new ShippingService(new ProductLocator(),
new PricingService(), new InventoryService(),
new TrackingRepository(new ConfigProvider()),
new Logger(new EmailLogger(new ConfigProvider())));
Without DI Container
DI Container
var svc = IoC.Resolve<IShippingService>();
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ProductLocator>().As<IProductLocator>();
builder.RegisterType<PricingService>().As<IPricingService>();
builder.RegisterType<InventoryService>().As<IInventoryService>();
builder.RegisterType<TrackingRepository>().AsSelf();
builder.RegisterType<ConfigProvider>().AsSelf();
builder.RegisterType<Logger>().AsSelf();
builder.RegisterType<EmailLogger>().As<IMessageLogger>();
var container = builder.Build();
Register all the dependencies
Resolve and get instance
var shippingService = container.Resolve<ShippingService>();
IKernel container = new StandardKernel();
container.Bind<IProductLocator>().To<ProductLocator>();
container.Bind<IPricingService>().To<PricingService>();
container.Bind<IInventoryService>().To<InventoryService>();
container.Bind<TrackingRepository>().ToSelf();
container.Bind<ConfigProvider>().ToSelf();
container.Bind<Logger>().ToSelf();
container.Bind<IMessageLogger>().To<EmailLogger>();
Register all the dependencies
Resolve and get instance
var shippingService = kernel.Get<ShippingService>();
public class HomeController : Controller
{
private readonly IHomeService _workerService;
public HomeController() : this(new HomeService())
{
}
public HomeController(IHomeService service)
{
_workerService = service;
}
}
Do you really need it all the time?