Strategy Pattern With Ninject
This is a follow-up to my post about avoiding dependencies with design patterns. It left off with something like this as a Cart object that uses the Strategy pattern to avoid a direct dependency on SMTP emails.
1: public class Cart
2: {3: private ISendEmail emailProvider;
4: //public Cart()
5: //{
6: // emailProvider = new LiveSmtpMailer();
7: //}
8: 9: public Cart(ISendEmail emailProvider)
10: {11: this.emailProvider = emailProvider;
12: } 13: 14: public void Checkout()
15: {16: // Do some other stuff
17: 18: this.emailProvider.SendMail("someuser@domain.com", "store@acme.com",
19: "Order Confirmation", "Your Order Was Received.");
20: } 21: }Note that I've commented out the default constructor - I'll explain why in a moment.
The next logical step in this series is to avoid the requirement of hard-coding in the class its default behavior of using a LiveSmtpMailer() instance to send email. We want to be able to manage these default implementations centrally, either via a class or some kind of configuration file. Tools that provide this functionality are collectively referred to as Inversion-of-Control containers, or IoC containers. Ninject is one such tool.
Download Ninject, build it if necessary, and add a reference to Ninject.Core to get started. With this in place, we can do the necessary plumbing work to have Ninject instantiate our Cart class for us, rather than using the new keyword ourselves directly. Avoiding the new keyword is a necessary consequence of most IoC containers, as far as I can tell. To tell Ninject what to do when it finds it needs an ISendEmail interface implementation, you can create something like the following:
1: public class CustomModule : StandardModule
2: {3: public override void Load()
4: { 5: Bind<ISendEmail>().To<ConsoleTestMailer>(); 6: } 7: }The ConsoleTestMailer is a new one I wrote for this example. It's listed here:
1: public class ConsoleTestMailer : ISendEmail
2: {3: public void SendMail(string To, string From, string Subject, string Body)
4: {5: Console.WriteLine("To: " + To);
6: Console.WriteLine("From: " + From);
7: Console.WriteLine("Subject: " + Subject);
8: Console.WriteLine("Body: " + Body);
9: } 10: }All it does is dump the email information to the console.
Now we're ready to write the Main() method of our console application:
1: class Program
2: {3: static void Main(string[] args)
4: {5: CustomModule module = new CustomModule();
6: IKernel kernel = new StandardKernel(module);
7: 8: Cart myCart = kernel.Get<Cart>(); 9: myCart.Checkout(); 10: } 11: }




Comments
Terje said on 25 Sep 2008 at 5:12 AM
Very fun to read your posts. I seem to follow your exact approach on understanding TDD, Interfaces and IoC. I haven't come to the point where I understand IoC containers still but I'm hoping to get there soon.
Nate Kohari said on 25 Sep 2008 at 10:54 AM
Nice post. :)
Sean Chambers said on 25 Sep 2008 at 10:46 PM
I think Nate may be biased there ;)
good post. I'm used to Windsor, so maybe I am missing something here with your call to kernel.Get<Cart>, I noticed that you didn't register Cart in the configuration, in ninject is it not necessary to register both the Cart AND ConsoleTestMailer in the ninject kernel?
another handy approach that I commonly use is setter injection for instances where the dependencies are optional. This way in my tests, I'm not required to pass in a bunch of mocks that I dont need for that test to complete. just thought I would add that to the mix!
Jeremy Gray said on 26 Sep 2008 at 6:32 PM
@sean - You can inject dependencies into concrete types without first registering them using StructureMap too. I suspect Autofac could do it as well, though I could be wrong on that. It seems to be becoming a rather common feature these days and, though I wouldn't want to become overly dependent on it, can come in quite handy (especially while migrating a codebase to progressively take more and more advantage of a DI container.)
Jeremy Gray said on 26 Sep 2008 at 6:35 PM
@sean - PS: Just say no to setter injection. That's just a hole in your app that means you are either exposing yourself to null reference exceptions or you are having to write lots of repeated instantiations of null/missing objects to be used when the setters are not set. Both of those options suck, IMHO.
Better to instead declare your dependencies up front and consistently inject them, even if that means going to an auto-mocking container to make test authoring a bit easier when your concrete types take a lot of (potentially-so-called-optional) dependencies.
Again, IMHO. ;)
Ryan Mettler said on 29 Sep 2008 at 7:54 AM
Hi Steve,
Love your posts! I tried this and could not believe how awesome this is/works. Props also to the Ninject team!
Now that I got the groupie love out of the way - my question:
What are your feelings regarding the performance cost associated with the reflection/code gen specifically using this in high scale/high volume sites.
Thanks