This document discusses dependency injection, including:
- Dependency injection is a technique for loosely coupling code through composition rather than inheritance and programming to interfaces rather than implementations.
- Volatile dependencies that introduce external configuration or are still in development should be injected, while stable dependencies will not introduce breaking changes.
- Different patterns for dependency injection include constructor injection, property injection, method injection, and ambient context.
- Anti-patterns to avoid include control freak, bastard injection, service locator, and constrained construction patterns.
- Frameworks like ASP.NET MVC, WPF, and WCF can implement dependency injection by composing applications in places like the global.asax file, OnStartup method, and service host factories
3. What is Dependency Injection?
What is Dependency Injection Not?
What Does Dependency Injection BuyYou?
Overview
4. What is Dependency
Injection?
A technique
A collection of patterns for loosely-coupled
code
Favour composition over inheritance
Program to the interface not the implementation
A form of Inversion of Control
5. What is Dependency Injection
Not?
Dependency Inversion (the D in SOLID)
You must first invert your dependencies to
properly apply dependency injection
Data Access
Layer
Business
Logic Layer
Presentation
Layer
6. What Does DI Buy You?
Testable and maintainable code
Encourages SOLID code
Late binding
Swapping services out for other implementations
Easy extensibility
Code can be extended in ways not originally
envisaged
7. The Purpose of DI
What to Inject (andWhat Not to Inject)
Concepts
14. What to Inject
VOLATILE DEPENDENCIES
libraries that:
introduce the need to configure an external
environment
are still in development
arent installed on all machines in the
development organisation
E.g.: EntityBroker, Phoenix, SpreadsheetGear
Inject across SEAMS
15. What Not to Inject
STABLE DEPENDENCIES
libraries or frameworks that:
wont introduce breaking changes with new
versions
you never expect to have to replace
E.g.: the BCL
17. Object Composition
Building object graphs into functional units
Creating objects
Creating relationships between objects
No more new Foo()
Single Responsibility Principle
Regain control through a DI container
Provides late-binding, extensibility and
parallel development benefits
18. Lifetime Management
Must invert control of an objects lifetime
management as well as the dependency itself
Different lifetime options
Singleton
Per-request
Per-thread
(and others)
19. A Word on IDisposable
Complicated by use of Dependency Injection
Do not mark your abstractions as
IDisposable
Let the DI Container handle disposable
implementations
Hide Ephemeral Disposables behind a SEAM
Use an injectedAbstract Factory to create them
Keep their using blocks short
21. Aspect-Oriented Programming
Cross-Cutting Concerns applied via attributes
decorating a method
[PrincipalPermission]
[HandleError]
Requires either:
Post-compilation step (e.g., PostSharp), or
A custom host (e.g.,WCF, ASP.NET MVC, NUnit)
22. AOP Disadvantages
Attributes are compiled with the code they
adorn
Cant easily change behaviour
Limited options for applying attributes
Attributes must have a simple constructor
Makes lifetime management trickier
23. Dynamic Interception
Can be achieved with liberal use of
Decorators
Violates DRY
Lots of boilerplate code
Container-level feature
CastleWindsor, Spring.NET, Unity and Ninject all
support it
Neat way to achieve DRY and SOLID code
25. Terminology
COMPOSITION ROOT
Where the application is assembled
DEPENDENCY
AVolatile Dependency from Part 1
DEFAULT value
E.g. a no-op, such as a Null Object
Not your default implementation!
26. Constructor Injection
This should be your default choice for DI
Guarantees that a necessary DEPENDENCY is
always available to the class
Advantages Disadvantages
Injection guaranteed Some frameworks make it
difficult
Easy to implement
27. Property Injection
Use when a DEPENDENCY is optional
There should be a good LOCAL DEFAULT
Advantages Disadvantages
Easy to understand Limited applicability
Not entirely simple to
implement robustly
28. Method Injection
Use when a DEPENDENCY varies per call of a
method
Usually the DEPENDENCY represents some
context for the method call
Limited applicability
Advantages Disadvantages
Allows the caller to provide
operation-specific context
Limited applicability
29. Ambient Context
Use to inject static DEPENDENCIES
Only use when querying the dependency
A proper LOCAL DEFAULT exists
It must be guaranteed available
Advantages Disadvantages
Doesnt polluteAPIs Implicit
Is always available Hard to implement correctly
May not work well in some
runtimes
31. Control Freak
DEPENDENCIES are controlled directly
E.g., creating an instance of Foo for a field of type
IFoo.
Usually occurs within a Factory
Root these out of your codebase!
Refactor to Constructor Injection
32. Bastard Injection
Foreign DEFAULTS are used as default values
for DEPENDENCIES
E.g., AccountController in ASP.NET MVC
Refactor towards Constructor Injection
Refactor towards Property Injection
But only if theres a LOCAL DEFAULT
33. Service Locator
Calling into your IoC container outside of the
COMPOSITION ROOT
Can tightly-couple your application to your
IoC container!
Acts as a replacement for new Foo()
Refactor towards Constructor Injection
34. Constrained Construction
Constructors are assumed to have a particular
signature
E.g.,WebForms requires Pages to have a default
constructor
No easy refactoring to DI
Some possibilities via Abstract Factory
38. ASP.NET MVC
COMPOSITION ROOT: global.asax +
IControllerFactory
Can also useWebActivator package from NuGet
Ignore IDependencyResolver!
Intended to be a Service Locator
Removes ability to manage object lifetime
IControllerFactory is the appropriate
extensibility point
Use Constructor Injection on your Controllers
39. WPF with MVVM
COMPOSITION ROOT:
Application.OnStartup()
Use Constructor Injection on your
MainWindowViewModel
Create an IWindow interface and inject this
Implemented by a MainWindowAdapter for each
Window
Create a MainWindowViewModelFactory
Needed because there is a circular dependency
between theView and theViewModel
41. ASP.NET
Suffers from Constrained Construction
COMPOSITION ROOT: each Page class
Single Responsibility Principle
Model-View-Presenter (MVP)
Keep Presenters fully independent of
System.Web
#5: Ultimately, its just a fancy name for passing stuff into classes. BUT its ramifications are significant.
#6: Dependency Inversion PrincipleHigh-level modules should not depend on low-level modules. Both should depend on abstractionsAbstractions should not depend on details. Details should depend upon abstractions.Your applications abstractions should be in the BLL, therefore it is wrong for the BLL to depend on the DAL.
#7: Encourages SOLID code = plays very nicely with SOLID code. SRP makes composing dependencies easier, amongst other things.
#9: DI is a means to achieving loosely-coupled codeThe TVspower is wired directly into the mains : its tightly coupled
#10: We can now plug in our laptop insteadUnplugging doesnt cause either the TV or the wall to explodeWeve added an interface!
#11: We get new functionalityWeve added a Decorator!
#12: We can now plug two things into the one socketWeve added a Composite!
#13: We can now plug in a foreign device, or something small like a camera or phoneWeve added an Adapter!
#14: Design patterns are building blocks, bricks. How you assemble your bricks mattersAnd this is the great thing about DI: you can add new functionality to new classes (obeying SRP and OCP via decorators), and include it by simply altering the way the application is put together.
#15: First one is interesting: EntityBroker is an obvious example, but less obvious examples include the .NET FileSystem classes. Not saying dont use these, dont rely on them: DO. Just inject them as dependencies to your class.A Seam is anywhere an interface is used over a concrete type.like the seams of a garment: where an application comes together. E.g., data access layer and business logic layer have a seam.
#16: Stable Dependencies: essentially any framework (e.g., ASP.NET MVC, Unit Testing), the BCL, IoC containersSome exceptions to the Dont inject the BCL rule: if a dependency is not easily mockable, such as HttpContext which is a sealed class, wrap it in an interface and Adapter, and inject that.
#17: Three dimensions of DI. Object Composition: building a graph of dependenciesLifetime Management: creating new instances of dependencies, scoping dependencies appropriately, correctly disposing of dependencies when they are no longer neededInterception:Application of the Decorator patternAspect-Oriented Programming
#18: Single Repsonsibility Principle states that a class should only have one reason to change. If a class is manually managing its dependencies by creating them, that is a reason to change. To adhere to the SRP, the class must surrender control of its dependencies; you regain control of those dependencies through the container.
#19: In the previous slide, we removed the ability to create dependencies. As a result, we lose the ability to dispose of them as well, and we must fully delegate management of the objects lifetime up the stack.Choosing the right one is important for application performance and avoiding resource leaksDo not confuse the Singleton lifetime with the Singleton pattern!
#20: Abstractions that are disposable are leaky abstractions: i.e., it betrays detail about the expected implementation behaviour. Even if you have a particular implementation in mind, avoid the urge to mark the interface as IDisposable. Its like saying your IMainsPlug is an IWatchable: you can watch a PS3, but itll be waaay less interesting than watching a TV. .NET Framework guidelines insist that if a class holds an IDisposable member, it should itself implement IDisposable so that it can dispose of the disposable members. If you have a dependency with Singleton scope injected into a class that, and the class follows this recommendation, you will start seeing ObjectDisposedExceptions all over your code.Ephemeral Disposables are common in WCF: channels are created and disposed for each service operation. This is a complicated topic and I cant do it proper justice here. See Chapter 8 for the full story.
#21: Cross-Cutting Concerns are areas of the application that need to be applied to all levels of the application, and that can be considered as common functionality required by many applications. I.e., NOT application-specificNote that they are separate from cross-cutting entities, which should be avoided. Cross-cutting entities are business objects valid in any layer of your application; however, when entities are allowed to travel between layers, the layers basically collapse. UI concerns and data access concerns will inevitably be mixed up. You may think you have layers, but you dont. (http://blog.ploeh.dk/2012/02/09/IsLayeringWorthTheMapping.aspx)E.g., Units
#22: Both [PrincipalPermission] and [HandleError] are examples from the .NET framework: the BCL and ASP.NET MVC respectively.
#23: Must re-compile code in order to change behaviour based on the attributes defined.Attributes can only be applied in certain places: type definitions, method definitions, etc.Attributes can only have simple constructors because of how they are used. Cant pass in instance variables, local variables, etc.
#24: Decorator isa great pattern for intercepting and modifying behaviour on the fly. However, it results in a lot of repetitive boilerplate code. We can instead take advantage of an IoC container-level feature, where supported, to achieve full dynamic interception.Container dynamically emits new types implementing the required aspect. You must still write the code to implement the aspect, but then you can just tell the container about the aspect and when to apply it. Usually a case of implementing an interface defined by the IoC container for the interceptor. Sometimes you might need to do the same for the interception itself as well. Ninject.Extensions.Interception provides IInterceptor and IInvocation interfaces. IInterceptor defines a single method, Intercept(IInvocation), but its recommended to use either ActionInterceptor or SimpleInterceptor if you can.
#26: Volatile dependencies = dependencies thatintroduce the need to configure an external environmentare still in developmentarent installed on all machines in the development organisationComposition Root is not always the application bootstrapper; in web applications, for example, it might be the beginning of a request. Depends on the framework youre using (if any).
#28: .NET Example: System.ComponentModel.IComponent.Site takes an ISite, which is mostly used by Visual Studio to support extra designer functionality.One of many ways of implementing the Open-Closed Principle.Example: Blog.BusinessLogic.Implementation.PostService.TagService (contrived example)
#29: No example in my Blog sample, just couldnt come up with one. Example in the book is one of a currency convertor.NET examples: System.ComponentModel.TypeConverter.ConvertTo() takes an ITypeDescriptorContext; IModelBinder.BindModel() takes a ControllerContext and a ModelBindingContext.Whilst Constructor Injection is useful for applications built on frameworks, Method Injection can be particularly useful for building frameworks themselves. Counter-example: IRatingAlgorithm.CalculateRating: neither the Rating nor the integer score parameters are dependencies, so this is not an example of Method Injection.
#30: Should only be used in the rarest of cases: please avoid static dependencies, not least because it makes unit testing really hard!Querying the dependency: dont use for e.g., Logging, where all methods of the dependency return void. This situation is better modelled using Interception.Examples from the BCL: Thread.CurrentPrincipal, Thread.Current[UI]CultureExample: LoggingService
#35: No easy sample to show for this one, unfortunately.
#36: Ayende talk, November 2010. A question he gets asked a lot is how he manages to do so much stuff. Aim: get to done as quickly and easily as possible.Nested Composite Design Very modular Layered from very low-level to high-level Manages complexity very well High orthogonality Requires some kind of container Self-composing Loose components Lends itself well to extensibilityWhy? Attack the problem in small increments whole is greater than sum of its parts naturally creates isolated pieces of code users can touch just one partDependency Injection enables this.
#39: IDependencyResolver is intended to be used as a Service Locator (Antipattern!), and is used by ASP.NET MVC as such. It also lacks a Release() method, meaning that objects cannot be cleaned up after use: weve lost the ability to manage object lifetime, and so it will cause resource leaks when used in conjunction with some DI containers like Castle Windsor. However, some containers (e.g. Ninject) implement IDependencyResolver to hook the container into MVC. This is ok as the use is tightly restricted and regulated; no dependencies other than the container, which has no lifetime difficulties of its own, are resolved this way.
#40: Poor design choice in WPF: DataContext is a property, which implies the dependency is optional. However, the DataContext is not optional when developing in MVVM, the pattern prescribed for WPF development by Microsoft!
#42: Hard work, but not impossible.Because the Page class is now our composition root, its responsibility is to compose the dependencies and we must delegate the actual page logic elsewhere. MVP the key. Keep Presenters in a separate PresentationLogic assembly with no reference to System.Web: thats the View layer.Alternative is to use a Service Locator, which is a Bad Thing, particularly here as it clouds the responsibility of the Page class. Unfortunately this is the approach that Viewer currently takes.