Tuesday, January 6, 2009

Inversion of Control or IoC

Inversion of Control or IoC is a design pattern. This pattern is much like the Strategy Design pattern from the GOF and the Dependency Injection pattern. I like this pattern for a few reasons. First: it allows for good separation of concerns. Second: if you are doing test driven development, this pattern is a must and last: if your implementation of an interface must be changed at runtime, this pattern is needed to receive the concrete object created by an object factory.

The IoC pattern is a minimum of three parts:
1. The concrete service class. This class is the primary interface to the client. I always suffix this class with service. Here you will define the main workflow. Methods in this class will invoke methods on the interface and perform the core logic of the service.
2. The provider interface. This gets passed to the service class when it is created. I always suffix this interface with provider.
3. The concrete provider. This class implements the provider interface. I always suffix this class with provider.

The following is an example of a simple IoC design pattern. This service provides the ability to perform home automation tasks.

public interface IHomeAutomationProvider
{
void TurnOn( int deviceId );

void TurnOff( int deviceId );

void DimLight( int deviceId, int percent );
}

public class HomeAutomationProvider : IHomeAutomationProvider
{
public void TurnOn( int deviceId )
{
// Turn on
}

public void TurnOff( int deviceId )
{
// Turn off
}

public void DimLight( int deviceId, int percent )
{
// Dim light
}
}

public class HomeAutomationService
{
private IHomeAutomationProvider _Provider;

public HomeAutomationService( )
:this( new HomeAutomationProvider())
{

}

public HomeAutomationService( IHomeAutomationProvider provider )
{
_Provider = provider;
}

public void TurnOn( int deviceId )
{
_Provider.TurnOn( deviceId );
}

public void TurnOff( int deviceId )
{
_Provider.TurnOff( deviceId );
}

public void DimLight( int deviceId , int percent )
{
_Provider.DimLight( deviceId , percent );
}
}

Notice the overloaded constructors on the service. This allows for ease of use if not using a factory to provide the provider and allows the service to have a default provider. If you are using TDD, and you do not have access to the API that performs the home automation task, or you do not want you lights going on and off when running tests you would simply create a new test or mock provider and pass it in to the service when performing the test.


References:
http://en.wikipedia.org/wiki/Strategy_pattern
http://martinfowler.com/articles/injection.html
http://www.dnrtv.com/default.aspx?showNum=126