Code: Learn about Inversion of Control

Inversion of Control

A common problem encountered in software development is the level of coupling which is formed within complex systems that slowly causes the system to become brittle and unwilling to change. This is seen by many as one of the factors that lead to a high maintenance cost of software.

To overcome this problem, an approach known as Inversion of Control (IoC) has been adopted as a favored way to loosely couple components in a software system so that components can be better encapsulated and they are flexible and responsive to change. Additionally we find that by structuring our systems in a loosely connected manner they are better able to be unit tested and constructed in a modular fashion which leads to better productivity for teams.

Inversion of Control describes the approach to handling a plug-in architecture. Rather than individual components being responsible for understanding other components which can handle a given request, their requirements will be satisfied by an external party who will inject the dependencies they need.

Contract First

To support a pluggable infrastructure we must avoid referring to a dependant component by its concrete class, but rather by an interface which describes the contract which it fulfills. For example, in BackgroundMotion we have the notion of an application controller class which implements the process flow for the domain. Controllers could become a dependency for anyone who might use them, so to avoid this we describe them first by a contract, and then implement concrete logic to meet that contract.

  public interface IContributionController : IController

  {

    /// <summary>

    /// View a <see cref="Contribution"/>

    /// </summary>

    Contribution ViewContribution(int id);

 

    /// <summary>

    /// Gets a <see cref="Contribution"/>.

    /// </summary>

    Contribution GetContribution(int id);

Above is an example from the IContributionController interface which is implemented by the ContributionController class. Consumers always refer to IContributionController.

Once we have established a set of contracts which bound components in our system we can use one of two common techniques to satisfy the relationships between those components through an inversion of control.

Dependency Injection

Dependency Injection is the most common software pattern for achieving Inversion of Control in software systems. To inject a dependency we can either take the dependency as a property to be assigned, an argument on a constructor or by a defined method on an interface. The injection itself is typically managed by an external provider usually referred to as an IoC container.

You can read more about the various types of Dependency Injection in an article from Martin Fowler. There are many frameworks which use Dependency Injection to manage coupling, we have listed a couple in the resources section below.

Service Locators

An alternate approach to achieve Inversion of Control is to use a gateway class known as the Service Locator which will find an appropriate class to meet a given dependency. This is the style of IoC which was used in BackgroundMotion and was prescribed and supported by the Composite Web application block.

To use the ServiceLocator, we ask it to return an instance of a class which implements a requested interface, e.g. to return an instance of an IContributionController we would write the following code.

  IContributionController controller =
      ServiceLocator.Get<IContributionController>();

  Contribution contribution = controller.GetContribution(id);

Services are initialized into the container by declaring a class which inherits from Microsoft.Practices.CompositeWeb.ModuleInitializer and then implementing and overridden Configure method. In BackgroundMotion our ModuleInitializer lives under the Mindscape.BackgroundMotion.Website.Infrastructure namespace.

Services are associated with the interface which they are implementing as shown below:

  services.Add<IUnitOfWorkFactory>(new DLinqUnitOfWorkFactory(Configuration.ConnectionString));

  services.Add<IRepositoryFactory>(new DLinqRepositoryFactory());

  services.Add<IReferenceDataProvider>(new ReferenceDataProvider(httpContextAdapter));

  services.Add<IMembershipController>(new MembershipController());

  services.Add<IContributionController>(new ContributionController());


Resources