Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Upcoming SlideShare
Osgi cdi
Osgi cdi
Loading in …3
×
1 of 47

Designing For Change

10

Share

Download to read offline

Inversion of Control via Dependency Injection - originally presented at Cleveland Day of .NET, May 17, 2008

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

Designing For Change

  1. Designing for Change Inversion of Control via Dependency Injection Nate Kohari
  2. About Me Software Architect at CTI in Akron, OH Creator of Ninject, a .NET dependency injection framework
  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); } }
  16. Cohesion = Good Coupling = Bad
  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. 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.
  29. 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.
  30. 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
  31. 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?
  32. 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
  33. Bindings (example) When IWeapon is requested, create a Sword: Bind<IWeapon>().To<Sword>(); When Samurai is requested, create a Samurai: Bind<Samurai>().ToSelf();
  34. 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)
  35. 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
  36. 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.
  37. 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
  38. Singleton (typical) class Shogun { private static Shogun _instance = new Shogun(); public static Instance { get { return _instance; } } private Shogun() { } public void RuleWithIronFist() { //... } }
  39. 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.
  40. Advanced Features Contextual binding: returns different implementations depending on situation Loose-coupled events via event broker Method interception (aspect-oriented programming)
  41. 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.
  42. 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.
  43. 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.
  44. 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.
  45. 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
  46. For More Information http://ninject.org/ http://kohari.org/ http://twitter.com/nkohari Questions?

×