Answering A C# Question

I often receive questions about topics covered in one or another of my books, and I respond, most often, privately. Every once in a while, however, a question comes along that may be of more general interest. This question, though about C#, has a profound effect on Silverlight programming as well…

Question:

… I still can’t figure out when I would need to create an interface when designing my application….

Short Answer

When you want to abstract out what is required from how that requirement is met.

Full Answer

The short answer is correct as far as it goes, but it doesn’t help at all [footnote 1]

The somewhat longer answer is to start with some of the premises of writing Clean Code:

  • Each method should be very short and do just one thing
  • Each class should have one easily articulated area of responsibility
  • Classes should know what other classes do but not how.

Let’s look at how that might play out…

You might start off by refactoring into a Note class and a FileManager class like this:

using System.IO;
using System;

namespace Interfaces
{
   class Program
   {
      static void Main( string[] args )
      {
         var np = new NotePad();
         np.NotePadMainMethod();
      }
   }

   class NotePad
   {
      private string text = "Hello world";

      public void NotePadMainMethod()
      {
         Console.WriteLine( "Here I would interact with you and offer you a writing surface" );
         Console.WriteLine( "Then when you push the right button, I ask FileManager to " );
         Console.WriteLine("print the file..." );

         var fm = new FileManager();
         fm.Print(text);
      }
   }

   class FileManager
   {
      public void Print(string text)
      {
         Console.WriteLine( "I'm pretending to backup the old version of the file and then " );
         Console.WriteLine( " print the text you sent me " );
         Console.WriteLine( " printing {0}" , text );
         var writer = new StreamWriter( @"c:\temp\HelloWorld.txt", true );
         writer.WriteLine( text );
         writer.Close();
      }
   }
}

This console program is stripped of all error checking and all three classes are in one file to keep things simple. The idea is that the NotePad class interacts with the user, obtains a string to print, and then sends it to the FileManager whose job is to see if the file exists, if so make a backup, and then write the user’s text to the file (presumably we’d open a file save dialog rather than just assuming the user wants to write to c:\temp\HelloWorld.txt).

Adding A Second Writer

After creating the above, you realize that there will be times that you will want to write to, e.g., Twitter.  You could put in a branching statement:

using System.IO;
using System;

namespace Interfaces
{
   class Program
   {
      static void Main( string[] args )
      {
         new NotePad().NotePadMainMethod();
      }
   }

   class NotePad
   {
      private string text = "Hello world";

      public void NotePadMainMethod()
      {
         var dest = "Twitter";
         switch ( dest )
         {
            case "File":
               var fm = new FileManager();
               fm.Print( text );
               break;
            case "Twitter":
               var tm = new TwitterManager();
               tm.Tweet( text );
               break;
         }
      }
   }

   class FileManager
   {
      public void Print(string text)
      {
         // write to file
      }
   }

   class TwitterManager
   {
      public void Tweet( string text )
      {
         // write to twitter
      }
   }
}

Adding An Interface

This begins to get ugly, and more important, the NotePad class is now entirely iStock_connectTwoWiresXSmalldependent on both the FileManager class and the TwitterManager class.  It is cleaner, easier to maintain, and far easier to test, if we remove those dependencies.

The first step in doing so is to have the NotePad not know which class will take care of writing the message; all it needs to know is that some class that knows how to “Write” will do the work.  We accomplish this by creating an interface, Writer[footnote b] that has a Write method.

   interface Writer
   {
      void Write(string whatToWrite);
   }

We then have the FileManager and TwitterManager classes implement this interface:

   class FileManager : Writer
   {
      public void Write( string text )
      {
         // write to a file
      }
   }

   class TwitterManager : Writer
   {
      public void Write( string text )
      {
         // write to Twitter stream
      }
   }

At this point, we return to the NotePad and it can instantiate the class it wants as a Writer:

      public void NotePadMainMethod()
      {
         var w = new TwitterManager();
         w.Write( text );
      }

That is step 1 in decoupling the NotePad from the other classes; now all it knows is that it has a Writer, which can be one or the other of the streams (or any other class that implements that method) but we’re hard coding which implementing class to use (in this case TwitterManager) in the NotePad class… not great.

Dependency Injection

iStock_InjectionRedXSmall Dependency Injection is one of those terms you really want to work into your conversation at every conference you attend. It marks you as a cutting edge, in the know kind of geek.

Here’s how it works.  You don’t want to hard-code the dependency into NotePad because that makes for code that is hard to maintain and hard to test (the technical term is “bad.”)

What you can do, instead, is “inject” the dependency at run time. You can inject in a number of ways, the most common of which are:

  • Constructor injection
  • Property injection
  • Parameter injection
  • Using a Factory
  • Using an Inversion of Control (IoC) container

(Yes, IoC container may be even cooler than dependency injection. More on IoC below, but not much more)

Constructor injection just says that we’ll let the Notepad know which type of writer it is going to use when we create the class:

   class NotePad
   {
      private string text = "Hello world";
      private Writer w;
      public NotePad( Writer w )
      {
         this.w = w;
      }

      public void NotePadMainMethod()
      {
         w.Write( text );
      }
   }

Notice that we pass in an instance of Writer, stash it away in a member variable and then NotePadMainMethod just uses it.  The actual instance is not created in NotePad, it is created in whomever instantiates the NotePad and “injected” into NotePad through the constructor.

Property Injection works the same way, but instead of passing in the writer through the constructor, you set a property,

      public Writer MyWriter { get; set; }

      public void NotePadMainMethod()
      {
         MyWriter.Write( text );
      }

Now whoever instantiates MyWriter just sets the property and NotePad can take it from there.

Parameter Injection, as you can, by now imagine, eschews having a member variable, and just passes the type of writer into the method as a parameter

      public void NotePadMainMethod(Writer w)
      {
         w.Write( text );
      }

Factory Pattern and IoC Containers

Each of these three approaches has its advantages, but none of them scale very well. To solve this problem, developers often turn to the Factory Pattern which allows for another level of indirection: an object that creates other objects (the factory).  We won’t go into that as it isn’t essential to understanding interfaces, but I will also mention that beyond the Factory, when you want the ability to “compose” your application at run time, with totally decoupled objects (that is, at run time you’ll decide what writer the NotePad will use, and neither the writer nor the NotePad need depend on the other) you can then turn to Inversion Of Control (IoC) containers; a big topic covered in some detail here, and here.

The Curve, and Being Behind It

Reading Scott’s great review of Alt.NET and Open Spaces and many topics related to the above; I am reminded of a cartoon in which you see a man and woman in a tree, while others are running by holding torches, and the woman says to the man, “How come it seems like everyone else is evolving but us?

More from me on Open Spaces, Dependency Injection, Alt.Net and much else coming soon. You can join me in working through all this while creating a truly amazingly fun open source project here, the documentation for which is here.

jessesig

—-

footnote 1: Old joke: two guys are in a helicopter over Washington state, and the fog comes in too fast; they are totally lost and running low on fuel.  The chopper pilot pulls up next to an office building, knocks on the window and when a man comes to the window, hollers “Where am I?”

The guy looks at him and says “You’re in a helicopter” and shuts the window.

The pilot flies directly to the airport and lands safely. His passenger, stunned, asks him, “how did that help?”  “Oh,” says the pilot, “I was in a life and death situation, and asked a perfectly reasonable question. He gave me an answer that was both true and totally unhelpful; so I knew right where I was: Microsoft Tech Support.”

(With apologies to the terrific and truly helpful people at tech support, who will please have a sense of kajf;320 <abend>)

footnote b: Notice that my interface is named Writer, not IWriter.  I’d like to say that I had the courage of my convictions to stop putting the Silly I in front of every interface (IWrite, IClaudius, IThinkThereforeIAm…) but it took reading Bob Martin’s wonderful book Clean Code to get me to step up.

This work is licensed under a Creative Commons license.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Mini-Tutorial and tagged , . Bookmark the permalink.

4 Responses to Answering A C# Question

Comments are closed.