3. Disclaimer
There are no
silver bullets
Use critical
thinking when
evaluating any
new idea
4. Goals
What is inversion of control (IoC)?
How can it make our software more
flexible?
What is dependency injection (DI)?
How can we use DI to achieve IoC?
Why should we use a DI framework?
5. Change Happens
Software seems like it should be easy to
change
As a result, people always find reasons to
change it!
Simple changes can be very difficult to
implement, and have widespread effects
6. Agile Software
This has given rise to Agile software
development methodologies
Project management strategies aren’t
enough
You need to build the flexibility into your
software itself
7. Divide and Conquer
When a problem is too complex, break it
into smaller, more easily-solvable
problems
Each problem is a concern
Separation of Concerns (SoC)
8. Divide and Conquer (cont.)
Think of your application like a jigsaw
puzzle
How should we carve it up?
How can we recombine the pieces?
Two measures to consider: cohesion and
coupling
9. Cohesion
How similar is the code within a given
class?
Highly-cohesive components = more
flexible software
Single Responsibility Principle (SRP)
10. Cohesion (BAD)
class Samurai {
public void Attack(string target) {
Console.WriteLine(“Chopped {0} in half!”,target);
}
}
11. Cohesion (better)
class Samurai {
public Sword Weapon { get; set; }
public void Attack(string target) {
Weapon.Hit(target);
}
}
class Sword {
public void Hit(string target) {
Console.WriteLine(“Chopped {0} in half!”,target);
}
}
12. Coupling
How much does a given concrete class
require other concrete classes in order to
operate?
Loosely-coupled components = more
flexible software
Interface Segregation Principle (ISP)
13. Coupling (BAD)
class Samurai {
public Sword Weapon { get; set; }
public void Attack(string target) {
Weapon.Hit(target);
}
}
class Sword {
public void Hit(string target) {
Console.WriteLine(“Chopped {0} in half!”,target);
}
}
14. Coupling (better)
class Samurai {
public IWeapon Weapon { get; set; }
public void Attack(string target) {
Weapon.Hit(target);
}
}
interface IWeapon {
void Hit(string target);
}
15. Coupling (better, cont.)
class Sword : IWeapon {
public void Hit(string target) {
Console.WriteLine(“Chopped {0} in half!”,target);
}
}
class Crossbow : IWeapon {
public void Hit(string target) {
Console.WriteLine(“Pierced {0}’s armor.”,target);
}
}
17. It’s All About Dependencies
Each piece of your app has other pieces
that it needs to operate (e.g. a Samurai
needs an IWeapon)
Once you break your app into pieces, you
have to glue the pieces back together
These are called dependencies
18. The Dependency Trap
class Samurai {
public IWeapon Weapon { get; }
public Samurai() {
Weapon = new Sword();
}
}
What’s wrong with this code?
19. The Dependency Trap (cont.)
class Samurai {
public IWeapon Weapon { get; }
public Samurai() {
Weapon = new Sword();
}
}
Samurai is still strongly coupled to Sword!
20. The Problem of Control
By creating the Sword within the Samurai
type, they’re still tightly coupled!
Objects should not be responsible for
creating their own dependencies
This is the idea behind inversion of control
21. Dependency Injection
One way of achieving inversion of control
Create the dependencies somewhere
outside the object, and inject them into it
Really just the Strategy pattern used en
masse
22. Dependency Injection
class Samurai {
public IWeapon Weapon { get; }
public Samurai(IWeapon weapon) {
Weapon = weapon;
}
public void Attack(string target) {
Weapon.Hit(target);
}
}
23. Dependency Injection (cont.)
class Program {
public static void Main() {
Samurai sam = new Samurai(new Sword());
sam.Attack(“the evildoers”);
}
}
This is dependency injection by hand.
24. Problems with DI by Hand
Wiring up objects by hand is cumbersome
What if your dependencies have
dependencies of their own, etc?
DI frameworks to the rescue!
25. DI Frameworks
Also called inversion of control containers
DI frameworks are like super-factories
Rather than wiring up dependencies
manually, just tell the framework how the
parts fit together
26. DI Frameworks (cont.)
Using a DI framework lowers the “cost” of
resolving dependencies to almost zero
This means you’re more likely to make the
appropriate separations
27. DI Frameworks (cont.)
Frameworks galore!
In Java: Guice, Spring, Pico, Nano
In .NET: Castle Windsor, StructureMap,
Spring.NET, ObjectBuilder/Unity
And of course my personal favorite...
28.
29. DI with Ninject
class Samurai {
[Inject] public IWeapon Weapon { get; set; }
public void Attack(string target) {
Weapon.Hit(target);
}
}
The [Inject] attribute marks the dependency.
30. DI with Ninject (cont.)
class Program {
public static void Main() {
IKernel kernel = new StandardKernel(...);
Samurai sam = kernel.Get<Samurai>();
sam.Attack(“the evildoers”);
}
}
When the Samurai is returned from the call to
Get(), it will already be “armed” with a Sword.
31. The Kernel
The kernel is Ninject’s super-factory
Your application should only have one
kernel (except in very complex cases)
All services should be created via the
kernel, either via Get() or an [Inject]
decoration
32. DI with Ninject (cont.)
class Samurai {
[Inject] public IWeapon Weapon { get; set; }
public void Attack(string target) {
Weapon.Hit(target);
}
}
But wait, IWeapon is just an interface!
How does Ninject know what to inject?
33. Bindings
Bindings are hints that you give to Ninject
Creates a map between a service type and
an implementation type
Lets you build future flexibility into your
code, and then adjust without changing
the code itself
Defined via Ninject’s fluent interface
34. Bindings (example)
When IWeapon is requested, create a Sword:
Bind<IWeapon>().To<Sword>();
When Samurai is requested, create a Samurai:
Bind<Samurai>().ToSelf();
35. DSL > XML
Other DI frameworks are geared around
defining bindings in XML
XML is verbose, ugly, and not much better
than plain text
Defining bindings via code lets you take
advantage of IDE features (auto-complete,
type-safety)
36. Modules
In Ninject, bindings are collected into
modules
Modules are just class definitions
In other containers, you end up with a
monolithic XML file
Your app can have any number of modules
37. Modules (example)
class WarriorModule : StandardModule {
public override void Load() {
Bind<IWeapon>().To<Sword>();
Bind<Samurai>().ToSelf();
}
}
Modules are just code, so you can build in as
much intelligence as you want.
38. Behaviors
Ninject can also help re-use instances
Singleton, one-per-thread, one-per-
request (web)
Separates instantiation behavior from
code, making it easier to change
39. Singleton (typical)
class Shogun {
private static Shogun _instance = new Shogun();
public static Instance {
get { return _instance; }
}
private Shogun() { }
public void RuleWithIronFist() {
//...
}
}
40. Singleton (Ninject)
[Singleton]
class Shogun {
public Shogun() { }
public void RuleWithIronFist() {
//...
}
}
The first time a Shogun is requested, it will be
activated, then on subsequent requests the
same instance will be returned.
41. Advanced Features
Contextual binding: returns different
implementations depending on situation
Loose-coupled events via event broker
Method interception (aspect-oriented
programming)
42. Contextual Binding
Bind<IWeapon>().To<Sword>(
When.Context.Target.Tag == “melee”
);
Bind<IWeapon>().To<Crossbow>(
When.Context.Target.Tag == “range”
);
These conditional bindings examine the
activation context of the current object.
43. Contextual Binding
class Swordsman {
[Inject, Tag(“melee”)]
public IWeapon Weapon { get; set; }
}
class Archer {
[Inject, Tag(“range”)]
public IWeapon Weapon { get; set; }
}
Contextual binding is useful for matching
identical type hierarchies.
44. Loose-Coupled Events
class Publisher {
[Publish(“SomeEvent”)]
public event EventHandler SomeEvent;
}
class Subscriber {
[Subscribe(“SomeEvent”)]
public void OnSomeEvent(object obj, EventArgs e)
{ ... }
}
Objects can subscribe to event channels
without knowing about actual instances.
45. Method Interception
class ComplexObjectFactory {
[Cache] public void Create() {
// Do something difficult to create object
}
}
Results in a chain of interceptors being called
before the method itself is executed.
46. Summary
Make your software more flexible by
maximizing cohesion and minimizing
coupling
Carve your app into a jigsaw puzzle
Use a DI framework like Ninject to glue
together the pieces