Philip Hendry's Blog

Software Dependencies

May 25, 2008 • Dev Tools, Design • 3 min read

Tools

Definition

Code must have dependencies - you wouldn’t get much done without referencing and depending on system libraries in the .Net Framework for example. However, whenever a component is designed the question of reversibility should be considered - how easy would it be to back-track on a particular design decision and replace it with another Maybe a better question is how can this be tested in isolation? In particular, testing in isolation speeds tests up and prevents an error in one component being raised back to another’s test case.

Dependency Inversion

There are a number of different ways to implement this and in particular Martin Fowlers article on this or Robert C. Martin’s. However, the basic premise is to implement to a contract - the contract being an interface definition. The concrete implementation of the interface can be created in a variety of ways and given to the component. For example, the following code uses a Service Locator:

class ServiceLocator
{
    public static T Find<T>()
    {
        return default(T);
    }
}

class InvoiceService
{
    private IAuthorizationService authorizationService;

    public InvoiceService()
    {
        this.authorizationService = ServiceLocator.Find<IAuthorizationService>();
    }
}

The Service Locator can decide itself where and how to generate the concrete implementations.

Dependency Injection

Instead of (or as well as) using a Service Locator you could create a parameterised constructor (or other mechanism) to pass the concrete dependencies into a component. This is especially useful when isolating a component for testing and passing it Mock Objects that can be used to confirm the correct interaction between components.

Inversion of Control Containers

IoC containers provide a central location to manage dependencies - in it’s simplest guise just a service locator with a different name. Here is an example of a simple IoC container or Dependency Resolver:

interface IDependencyResolver
{
    T Resolve<T>();
}

class SimpleDependencyResolver : IDependencyResolver
{
    public readonly Dictionary<Type, object> m_Types =
        new Dictionary<Type, object>();

    public T Resolve<T>()
    {
        return (T)m_Types[typeof(T)];
    }

    // Register is not defined on the interface because only
    // the creator need call it
    public void Register<T>(object obj)
    {
        if (obj is T == false)
        {
            throw new InvalidOperationException();
        }
        m_Types.Add(typeof(T), obj);
    }
}

Static Gateway

Rather than pass the Dependency Resolver to every component it’s easier to define a Static Gateway:

class DependencyResolver
{
    private static IDependencyResolver s_Inner;

    public static void Initialize(IDependencyResolver resolver)
    {
        s_Inner = resolver;
    }

    public static T Resolve<T>()
    {
        return s_Inner.Resolve<T>();
    }
}

This leads to the option that each class’s default constructor can now create the concrete classes it needs without knowing where they came from:

class InvoiceService
{
    private IAuthorizationService authorizationService;

    public InvoiceService()
    {
        this.authorizationService = DependencyResolver.Resolve<IAuthorizationService>();
    }
}

class Program
{
    static void Main(string[] args)
    {
        SimpleDependencyResolver s = new SimpleDependencyResolver();
        s.Register<IAuthorizationService>(new AuthorizationService());

        DependencyResolver.Initialize(s);
        InvoiceService invoiceService = new InvoiceService();
    }

}
Post by: Philip Hendry