Avoiding Dependencies

I gave a one day class to about 20 developers today introducing Microsoft .NET, C#, and ASP.NET. As it was only one day and there were no hands-on labs, coverage was necessarily cursory, but overall things went very well.

In the course of discussing the Base Class Library and specifically the areas of logging and sending emails (System.Diagnostics and System.Net), I was careful to emphasize to the class that they should definitely avoid making direct calls to these namespaces' members for their logging and emailing needs.  Instead, they should encapsulate their dependency on these classes using one of two patterns (and I'm inclined to favor the latter).  Specifically with regard to email, I gave the example that testing the application in a staging environment might really send out emails if there were no way to swap out that functionality.  It was clear from their reactions that several of the developers in the room could directly relate to this scenario.

Before showing the fix, let me demonstrate the problem by way of example.  Let's say you have a need to send emails in your application.  You might have the following code inside a Cart class as part of an online store.

Tightly Coupled Code

   1: public void Checkout()
   2: {
   3:     // Do some other stuff
   5:     System.Net.Mail.MailMessage myMessage =
   6:         new MailMessage("store@acme.com",
   7:             "someuser@domain.com",
   8:             "Order Confirmation",
   9:             "Your Order Was Received.");
  10:     SmtpClient mySmtpClient = new SmtpClient("localhost");
  11:     mySmtpClient.Send(myMessage);}
  12: }

This works.  It's simple.  The problem is, it's quite inflexible and it's tightly coupled to System.Net.Mail.  If you have code like this all over your application, and you find a need to switch to another class for sending emails (which anyone who depended on System.Web.Mail should relate to, as it's been deprecated in favor of System.Net.Mail now), you'll have to touch many different parts of your application to make that one change.  That definitely violates the DRY principle.

Facade Pattern (reference)

Create a simple wrapper of the functionality required, exposing only those methods needed by your application.  This has the effect of simplifying the API as well as limiting the dependency on the underlying class to one location.  The resulting wrapper need not be static, but if we take a very simple example that sends an email, it might look like this:

   1: public static class Mailer
   2: {
   3:     public static void SendMail(string To, string From, 
   4:         string Subject, string Body)
   5:     {
   6:         System.Net.Mail.MailMessage myMessage =
   7:             new MailMessage(From, To, Subject, Body);
   8:         SmtpClient mySmtpClient = new SmtpClient("localhost");
   9:         mySmtpClient.Send(myMessage);
  10:     }
  11: }

With this wrapper class in place, the original tightly coupled code could be written simply as follows:

   1: public void Checkout()
   2: {
   3:     // Do some other stuff
   5:     Mailer.SendMail("someuser@domain.com", "store@acme.com",
   6:         "Order Confirmation", "Your Order Was Received.");
   7: }

Strategy Pattern (reference)

The Strategy pattern essentially describes one of the most common techniques for performing Dependency Injection.  Using the Strategy pattern to eliminate the dependency on System.Net.Mail, we would define an interface and then push that interface into the constructor of the class that currently is carrying the dependency.  So if we imagine that this Checkout() method resides on a Cart class, we would modify the Cart class to include a private member variable of the interface's type and create one or more constructors allowing this interface to be passed in when the object is created.  Within the constructor, the local instance of the interface is assigned to the one being passed in.  This is rather more involved, but far more powerful, than the simple static wrapper class approach, and has other benefits as well (like support for swapping out implementations without having to rebuild the application).

Let's start with the interface.  All we need it to do at the moment is send email, so we'll call it ISendEmail.

   1: public interface ISendEmail
   2: {
   3:     void SendMail(string To, string From, string Subject, string Body);
   4: }

Now we can create a class that implements this interface.  The Mailer class nearly does so - removing the static keywords does the trick (and noting adding the reference to the dependency, of course).  We'll also rename it so it's clear that it really is sending mails via Smtp.

   1: public class LiveSmtpMailer : ISendEmail
   2: {
   3:     public void SendMail(string To, string From, string Subject, string Body)
   4:     {
   5:         System.Net.Mail.MailMessage myMessage =
   6: new MailMessage(From, To, Subject, Body);
   7:         SmtpClient mySmtpClient = new SmtpClient("localhost");
   8:         mySmtpClient.Send(myMessage);
   9:     }
  10: }

Next we need to modify the Cart to accept an ISendEmail instance via its constructor.

   1: public class Cart
   2: {
   3:     private ISendEmail emailProvider;
   4:     public Cart()
   5:     {
   6:         emailProvider = new LiveSmtpMailer();
   7:     }
   9:     public Cart(ISendEmail emailProvider)
  10:     {
  11:         this.emailProvider = emailProvider;
  12:     }
  13: }

Notice that the parameterless constructor now sets the ISendEmail local instance to LiveSmtpMailer, so its behavior remains unchanged by default.  If you have existing code that has hard dependencies on concrete implementations, you can leave their funtionality in place using this technique, but still provide the capability to alter the behavior by adding additional constructor overloads (or, alternately, property setters).

At this point we're done.  But for the latter case let's go one step further and say we want to write a test for Checkout().  In the original and in the Facade pattern implementation, there is no way to remove the dependency on System.Net.Mail from the code as it is written.  Thus, any tests written would need to be hacked to look for Smtp connection exceptions, or worse, they might actually send out emails.  Compare that with the Strategy / Dependency Injection approach, which can easily be tested by mocking ISendEmail or by creating a simple test stub, like the following.

   1: public class TestStubEmailer : ISendEmail
   2: {
   3:     public int MessagesSent;
   4:     public string LastMessageTo;
   5:     public string LastMessageFrom;
   6:     public string LastMessageSubject;
   7:     public string LastMessageBody;
   9:     public void SendMail(string To, string From, string Subject, string Body)
  10:     {
  11:         MessagesSent++;
  12:         LastMessageTo = To;
  13:         LastMessageFrom = From;
  14:         LastMessageSubject = Subject;
  15:         LastMessageBody = Body;
  16:     }
  17: }

Writing the test is now a simple matter.

   1: [Test]
   2: public void CheckOutTest()
   3: {
   4:     string testTo = "test@test.com";
   5:     string testFrom = "test2@test2.com";
   6:     string testSubject = "Test Subject";
   7:     string testBody = "Test Body";
   8:     TestStubEmailer myEmailer = new TestStubEmailer();
   9:     Cart myCart = new Cart(myEmailer);
  10:     myCart.Checkout();
  11:     Assert.AreEqual(testTo, myEmailer.LastMessageTo);
  12:     Assert.AreEqual(testFrom, myEmailer.LastMessageFrom);
  13:     Assert.AreEqual(testSubject, myEmailer.LastMessageSubject);
  14:     Assert.AreEqual(testBody, myEmailer.LastMessageBody);
  15:     Assert.AreEqual(1, myEmailer.MessagesSent);
  16: }

Not Just For Testing

But there's more benefit from the Strategy pattern approach than just testing.  In fact, let me revisit the initial problem being discussed, which is that the application should not really send out emails when it is being tested, but there should be some way for the developers to know that emails would have been sent.  Using the Strategy pattern approach (and especially if you couple it with an IoC container to make things easy), it would be a simple matter to have the default implementation of ISendEmail depend on a configuration setting.  In a Stage environment, an implementation of ISendEmail that actually just logs the messages to an event log could be configured, while in production the configuration could wire up the actual LiveSmtpMailer class.  Never again would you need to worry about accidentally sending emails out to customers while testing the application from the stage server.  Clearly the result is more flexible code that is easier maintain, configure, and test.

It's only been in the last year or so that I've really fallen in love with the Strategy pattern.  Capabilities like this, as well as the ability to potentially swap out implementations using configuration or IoC containers, make it a much more flexible approach than simply inserting a concrete Facade class between the dependency and its consumers (which is how I typically approached the problem previously).  If you find yourself struggling to remove dependencies in your application, the Strategy pattern as shown here may be helpful for you.

Update: Part 2 wraps up with the Strategy Pattern With Ninject

kick it on DotNetKicks.com

blog comments powered by Disqus