Friday, April 15, 2011

The Inversion of Control Pattern


The term Inversion of Control (IoC) is a computer programming technique wherein the flow of the control of an application is inverted. Rather than a caller deciding how to use an object, in this technique the object called decides when and how to answer the caller, so the caller is not in charge of controlling the main flow of the application.

This approach makes your code flexible enough to be decoupled. It can be unaware of what is going on in the call stack because the called object doesn’t need to make any assumptions about what the caller is doing.
The Dependency Injection pattern is simply a concrete implementation of the IoC .Unfortunately, as Martin Fowler specifies in his book, there is a lot of confusion about these terms, because the common IoC containers available for languages such as Java or .NET are typically identified as IoC containers, but the techniques implemented in your code when you use these frameworks is the Dependency Injection pattern, which is just one of the available concrete implementations for IoC. For example, if you plan to work with a modularized WPF/Silverlight application using a well-known framework such as Prism, you might implement IoC using the Service Locator pattern and not Dependency Injection because you need a global IoC container available for all the modules. Imagine that you have a simple LogWriter concept that is used to write a log message either to a specific database table or to a specified file. You might depict this as shown in Figure 1.

Figure 1 - Basic structure of a multi-targeted log system.


The UML diagram in Figure 1 is pretty clear; there’s an abstract BaseLogger class that exposes a WriteLog message, and two concrete classes that inherit from BaseLogger. These expose the method in two ways: one writes a log message to a database, the other to the file system. The following code shows the wrong way to use one of these concrete loggers— without applying an IoC implementation:
static void Main(string[] args)
{
/* Wrong way
*
* */
var firstLogger = new FileLogger();
firstLogger.WriteLog("Some Text.");
var secondLogger = new DatabaseLogger();
secondLogger.WriteLog("Some other Text.");
Console.ReadKey();
}

The biggest problem with this approach—not applying an IoC implementation—is that if you want to specify a different log at runtime, you’ll need to rewrite some code. That’s a huge architectural constraint. For example, suppose that you want to get rid of the FileLogger object. That’s not easy. You can’t simply eliminate it, because the application wouldn’t execute any more, or at least, you would need to modify and recompile it for it to continue working.
To solve the problem, the first step is to decouple the existing hierarchy by using an interface instead of the base abstract class, as illustrated in Figure 2. This way, you simply define a contract between a concrete log and its interface. Subsequently, to write a log message to a different location, you just need to render the interface in a specific way.


Figure 2 Refactoring the LogWriter using a common interface.


The code that follows is a refactored version that uses an IoC approach to declare the type of logger to be used at runtime. This approach is still procedural, because it decides which logger to use, but at least it decouples the code, so this is a somewhat more flexible version of the custom writer.
/// <summary>
/// Custom writer that can uses any log
/// </summary>
public sealed class Writer
{
/// <summary>
/// Accessor to the injected logger
/// </summary>
private ILogger logger;

/// <summary>
/// Initializes a new instance of the <see cref="Writer"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
public Writer(ILogger logger)
{
this.logger = logger;
}

/// <summary>
/// Writes the specified message.
/// </summary>
/// <param name="message">The message.</param>
public void Write(string message)
{
this.logger.WriteLog(message);
}
}

At this point, you need something between the application and the logger that can resolve which logger to use at runtime. The following example uses procedural code to do that without using the Dependency Injection or the Service Locator patterns:
static void Main(string[] args)
{
// IoC without an IoC container
var firstLogger = new FileLogger();
//Injectin of a specific logger
var writer = new Writer(firstLogger);
writer.Write("Log for the File system.");
Console.ReadKey();
}
An alternative solution would be to implement the Dependency Injection or the Service Locator.



No comments:

Post a Comment