Thursday, 22 December 2011

Inversion of Control - Simple Logging Engine Example

Define an interface ILogger. The idea being that different implementations of the ILogger class will log to different mediums (e.g. console or a file).

public interface ILogger
{
     void OpenLog();
     void CloseLog();
     void Log(string message);
}
Here's an example implementation of ILogger that outputs to console (or default out):

public class ConsoleLogger : ILogger
{
     public void OpenLog()
     {
          //Don't need to do anything
     }
     public void CloseLog()
     {
          //Don't need to do anything
     }
     public void Log(string message)
     {
         Console.WriteLine(message);
     }
}
And this example implementation of ILogger logs to a file:

public class FileLogger : ILogger
{
     private StreamWriter sw; 
     public void OpenLog()
     {
          sw = new StreamWriter(@"C:\FileLogger.log", true);
          sw.AutoFlush = true;
     }
     public void CloseLog()
     {
         if (sw != null)
         {
              sw.Close();
              sw.Dispose();
              sw = null;
         }
     }
     public void Log(string message)
     {
         if (sw != null)            
              sw.WriteLine(message);
     }
}
And now for the logging engine itself:
public class LoggingEngine
{
     private ILogger logger;
     public LoggingEngine()
     {
          this.logger = new ConsoleLogger(); //Default console 
     }
     public LoggingEngine(ILogger logger)
     {
          this.logger = logger; //Inject a different logger
     }
     public void Log(string message)
     {
          this.logger.OpenLog();
          this.logger.Log(message);
          this.logger.CloseLog();
     }
}
Finally, the two lines below make a log entry by specifying an ILogger implementation (a FileLogger in this case):

var loggingEngine = new LoggingEngine(new FileLogger());
loggingEngine.Log("Hello, world!");
As the above two lines show, you can now swap in/out a new ILogger implementation with ease, therefore reducing the coupling between your logging functionality and the rest of your application. We can also easily instantiate loggingEngine with the default constructor and log to standard output by default.