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: }
14 Comments
Terje said
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
Nice post. :)
Sean Chambers said
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
@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
@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
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
beloed said
Hi steve, that is good solution with that countainer code. thanks.
Alvaro said
I'm sorry, but I don't get the point of this added complexity. Perhaps if you could please rephrase your last sentence for me, I would have a clearer understanding of why all of this is better than having a default constructor with ConsoleTestMailer as the default emailProvider. Thanks!
ssmith said
@Alvaro,
The complexity of adding NInject is minor. The advantage of using an Inversion of Control Container like NInject is that it allows you to take control of your object dependencies and manage them centrally. As you move from one environment to another, or plug in a new provider for something in one version or another of your application, you can do so by touching only the part of your application that is responsible for these dependencies, rather than in various constructors scattered throughout your code.
Technically, having a class be responsible for knowing the default implementation of any dependencies it has is a violation of the Open Closed Principle and Separation of Concerns. The class should really just be exposing the interface of the dependency it needs, and should otherwise be ignorant of how that need is filled by the application/caller. The responsibility for deciding how to fill that dependency rests on the IoC container, and that is its sole responsibility.
Craig said
ok - a few questions :)
In your first example emailProvider is already a provider so it could be swapped out with another implementation without changing the cart class, right? so does the problem still exist?
Also for things like this I like to have a simple, central library - for example Core.SendMail - that method is called from any location and the method implementation can be changed, centrally at any time.
I can't help but feel a problem is being solved that maybe wasn't there. Could you define "centrally" in this example - is the module now responsible for binding the emailProvider?
Thanks!
ssmith said
@Craig,
The problem being solved is the violation of DRY (Don't Repeat Yourself) and OCP (Open/Closed Principle) that having the default implementation in the class presents. It may not be clear if you think that this is the *only* class that is using a particular concrete implementation of an interface, but imagine that instead we're talking about an ILogger that is used by every class in an application. If you hard-code that to be a FileSystemLogger (for instance) in every class, and later change your mind to use a DatabaseLogger, you need to touch *every class*. By storing the logic of deciding what the default implementation is in a single canonical location, your design is more flexible and less brittle.
Craig said
@Steve, Agreed - let me play devils advocate if I may.
Lets use the ILogger example - I would create a method called Core.LogError which internally had the FileSystemLogger. Only Core.LogError gets called by all my application touch points. Should I ever need to say use a DatabaseLogger I just change the LogError method in my central location. It obeys DRY for sure. How in this case could I make my code better by having an IoC container create my logger class? If that is the correct logic :) thanks again.
ssmith said
@Chris,
It sounds like you're basically arguing the benefits of the Facade pattern vs. the Strategy pattern. Both are useful abstractions and good ways to consolidate logic. The benefits of Strategy over Facade are reduced coupling (better testability) and fewer places to make changes if you need to change the implementation used by various classes (consider if you want half your classes using one implementation and another half using another). Also if you are literally calling Core.LogError as a static method throughout your application, and that implementation is (let's say) talking to a database, now all of your tests have a dependency on a database (tight coupling). Even though you achieve DRY, you are violating DIP because you depend on concretions not abstractions.
If you are still using Strategy and injecting your Core.LogError instance into the classes, then the IoC container doesn't offer you all that much on top of this. However, while you may not be duplicating which Logger you use in more than one place, you are duplicating the broader question of where you decide which concretions compose your application (spreading the logic throughout Core and perhaps elsewhere). With an IoC container in place, the composition of your application is defined in one location, so changes to it can easily be found and less chance exists for things to be missed or mistakenly duplicated.
Sunit Joshi said
Hi Steve
I'm trying to do the same thing but somehow it is not working for me. Can you please check out my query on stackoverflow
<a target="_blank" href="http://stackoverflow.com/questions/1359509/ninject-class-instantiation">stackoverflow.com/.../ninject-class-i</a>
if you have time ?
thanks
Sunit