Dependency Inversion
Telerik Academy Alpha
HQC
Table of contents
Dependency Inversion?
(Inversion of Control)
What?
- The term Inversion of Control (IoC) originally meant any sort of programming style
where an overall framework or runtime controlled the program flow. - According to that
definition, most software developed on the .NET Framework uses IoC. - In short, IoC is a much broader term that includes, but isn’t limited to, DI.
Why?
- High-level modules should not depend on low-level modules.
- Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.
-
You know that it’s not reasonable for a class to have zero dependencies-to have zero coupling.
- The Dependency Inversion Principle is the key to this goal.
How?
- Consider a set of classes that need to be instantiated into the correct hierarchy so that you can get the functionality needed.
- This creates the necessary hierarchy but couples the classes together, directly.
- You would not be able to use Foo without bringing Bar along with it.
How?
- Decouple the implementation of Bar from the use of it in Foo by introducing the interface
- However, you’ve only decoupled the implementation by separating the interface from it.
- You may have decoupled the implementation of Bar, but you have left Foo dependent on changes to Bar.
How?
- Policy owns the abstraction. Detail depends on policy.
- If Foo owns the IBar abstraction, you can place these two constructs in a package that is independent of Bar.
- When you change the implementation of Bar, you are no longer seeing an upward ripple of changes.
Dependency Injection?
What?
- Dependency Injection is a set of software design principles and patterns that enable us to develop loosely coupled code.
- Program to an interface, not an implementation.
- DI is nothing more than a technique that enables loose coupling. However, there
are many misconceptions about DI, and sometimes they get in the way of proper
understanding. - Many people refer to DI as INVERSION OF CONTROL (IoC). These two terms are sometimes used interchangeably, but DI is a subset of IoC.
How to explain it?
- How to explain Dependency Injection to a 5-year old.
-
When you go and get things out of the refrigerator for yourself,
you can cause problems. You might leave the door open, you
might get something Mommy or Daddy doesn’t want you to
have. You might even be looking for something we don’t even
have or which has expired.
What you should be doing is stating a need, “I need something
to drink with lunch,” and then we will make sure you have
something when you sit down to eat.
Wise Guy in stackoverflow.com
Why?
-
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
Myths
-
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
-
Benefits
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) |
Real life example
-
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
Example
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
Example
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
Example
-
Define the abstraction (interface)
-
It could have other methods as well
-
Required method
public interface IMessageWriter
{
void Write(string message);
}
Example
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
DI(IoC) Containers
What?
- A DI CONTAINER is a software library that can automate many of the tasks involved in
composing objects and managing their lifetimes. - Don’t expect a DI CONTAINER to magically make tightly coupled code loosely coupled. A DI CONTAINER can make the use of DI more efficient, but an application must first and foremost be designed with the DI patterns
and techniques in mind.
Why?
- Imagine that you want to resolve an instance of the class.
- You could suply it manually
- But you have no control over lifetime of the object
- And resolving all the dependencies in the graph
- Much work to handle it manually
- You could suply it manually
- You could use container which provides you with all the functionality needed to control the object creation and management
How?
- Resolve an instance of the concrete class
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>();
How? (Autofac syntax)
- How DI Container knows what we want?
- It is not guessing or coin flipping
- You tell him what you want!
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>();
How? (Ninject syntax)
- How DI Container knows what we want?
- It is not guessing or coin flipping
- You tell him what you want!
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>();
Poor man's DI
- How would you inject a worker service in a controller class and perhaps a repository in a worker service?
public class HomeController : Controller
{
private readonly IHomeService _workerService;
public HomeController() : this(new HomeService())
{
}
public HomeController(IHomeService service)
{
_workerService = service;
}
}
Poor man's DI
- The most obvious way is by letting controllers and services create a fresh new instance of the dependent objects.
- This route, however, creates a tight dependency between objects that potentially hinders extensibility and testability.
- For this reason, many suggest inversion of control and IoC containers.
When?
- The point of the diagram is that Poor Man's DI can be valuable because it's simple, while a DI Container can be either valuable or pointless depending on how it's used.
Do you really need it all the time?
Containers Catalog
Containers
- AutoFac
- Ninject
- Simple Injector (the fastest of all contestants)
- Spring.NET
- StructureMap
- Unity
- Windsor
Containers
- How to choose one?
- It depends on your personal choice
- Or the choice someone of your team made before you
- You could find many performance comparisons and decide based on them
- We will use Ninject throughout the course
- Ninject is certainly not the fastest, but it is one of the most flexible and extensible framework
Questions?
[C# HQC] Dependency Inversion
By telerikacademy
[C# HQC] Dependency Inversion
- 1,051