Slideshow transcript
Slide 1: Designing for Change Inversion of Control via Dependency Injection Nate Kohari
Slide 2: About Me Software Architect at CTI in Akron, OH Creator of Ninject, a .NET dependency injection framework
Slide 3: Disclaimer There are no silver bullets Use critical thinking when evaluating any new idea
Slide 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?
Slide 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
Slide 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
Slide 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)
Slide 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
Slide 9: Cohesion How similar is the code within a given class? Highly-cohesive components = more flexible software Single Responsibility Principle (SRP)
Slide 10: Cohesion (BAD) class Samurai { public void Attack(string target) { Console.WriteLine(“Chopped {0} in half!”,target); } }
Slide 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); } }
Slide 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)
Slide 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); } }
Slide 14: Coupling (better) class Samurai { public IWeapon Weapon { get; set; } public void Attack(string target) { Weapon.Hit(target); } } interface IWeapon { void Hit(string target); }
Slide 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); } }
Slide 16: Cohesion = Good Coupling = Bad
Slide 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
Slide 18: The Dependency Trap class Samurai { public IWeapon Weapon { get; } public Samurai() { Weapon = new Sword(); } } What’s wrong with this code?
Slide 19: The Dependency Trap (cont.) class Samurai { public IWeapon Weapon { get; } public Samurai() { Weapon = new Sword(); } } Samurai is still strongly coupled to Sword!
Slide 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
Slide 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
Slide 22: Dependency Injection class Samurai { public IWeapon Weapon { get; } public Samurai(IWeapon weapon) { Weapon = weapon; } public void Attack(string target) { Weapon.Hit(target); } }
Slide 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.
Slide 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!
Slide 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
Slide 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
Slide 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...
Slide 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.
Slide 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.
Slide 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
Slide 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?
Slide 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
Slide 34: Bindings (example) When IWeapon is requested, create a Sword: Bind<IWeapon>().To<Sword>(); When Samurai is requested, create a Samurai: Bind<Samurai>().ToSelf();
Slide 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)
Slide 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
Slide 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.
Slide 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
Slide 39: Singleton (typical) class Shogun { private static Shogun _instance = new Shogun(); public static Instance { get { return _instance; } } private Shogun() { } public void RuleWithIronFist() { //... } }
Slide 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.
Slide 41: Advanced Features Contextual binding: returns different implementations depending on situation Loose-coupled events via event broker Method interception (aspect-oriented programming)
Slide 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.
Slide 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.
Slide 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.
Slide 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.
Slide 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
Slide 47: For More Information http://ninject.org/ http://kohari.org/ http://twitter.com/nkohari Questions?




Add a comment on Slide 1
If you have a SlideShare account, login to comment; else you can comment as a guest- Favorites & Groups
Showing 1-50 of 3 (more)