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