Inversion of Control Using Generics - Revisiting the Separation of Use and Implementation

Martin Fowler in his famous article "Inversion of Control Containers and the Dependency Injection pattern" has compiled a number of ways how to dynamically bind a client to a service. I now would like to add two points to the discussion: firstly a distinction regarding what is injected, and secondly a new pattern for injection based on generics. During my discusson I´ll use the same sample scenario as Martin to make it easy to see what I´m trying to add. Here´s a quick recap using C#.

Martin is talking about a client component or class called a MovieLister which is supposed to filter a list of movies to find the ones directed by a particular director:

class MovieLister

{

    IMovieFinder finder;

   

    public Movie[] MoviesDirectedBy(string directorName)

    {

        List<Movie> resultset = new List<Movie>();

 

        foreach (Movie m in finder.FindAll())

            if (m.Director == directorName)

                resultset.Add(m);

 

        return resultset.ToArray();

    }

}

 

class Movie

{

   

    public string Director { get { … } }

}

 

Now, this client class is depending on a service managing all the movies and getting a list of them. To the client this service is only known through an interface, though – IMovieFinder –, to decouple it from any concrete implementations:

 

interface IMovieFinder

{

    List<Movie> FindAll();

}

 

The question Martin´s article revolves around is: How does the MovieLister get at an instance of IMovieFinder? Obviously it´s no option to just instanciate, say, a class MovieFinder, which might implement the interface IMovieFinder in MovieLister´s constructor. That would make MovieLister dependent on a particular implementation of the interface and is exactly what´s to be avoided to keep the MovieLister class easily testable.

 

Martin´s answer consists of a number of patterns of how to inject an instance of a IMovieFinder implementation into the MovieLister. I won´t repeat them here in detail but rather just show a code snippet to bring them into the world of C#. For explanations see Martin´s article.

Constructor Injection

Pass in an instance of IMovieFinder when creating a MovieLister:

 

class MovieLister

{

    public MovieLister(IMovieFinder finder)

    {

        this.finder = finder;

    }

   

Setter Injection

Set the finder of a MovieLister object at any time through a property or setter method:

 

class MovieLister

{

    public IMovieFinder Finder

    {

        set

        {

            this.finder = value;

        }

    }

   

Interface Injection

Separate the injection method into an interface of its own:

 

interface IInjectMovieFinder

{

    void InjectFinder(IMovieFinder finder);

}

 

class MovieLister : IInjectMovieFinder

{

    public void InjectFinder(IMovieFinder finder)

    {

        this.finder = finder;

    }

            

Service Locator

Ask a service locator object or class to provide an instance:

 

static class ServiceLocator

{

    public static IMovieFinder GetMovieFinder()

    

       

    }

}

 

class MovieLister

{

    public MovieLister()

    {

        this.finder = ServiceLocator.GetMovieFinder();

    }

   

Type Injection or Instance Injection

As valuable as these patterns are, I found their discussion somewhat incomplete, since it lacks a distinction. Martin´s article seems to be talking only about instances of a service described by an interface. But in fact it´s not that simple.

 

True, the injection patterns push an instance of an interface implementation into the client object. But what does the service locator do? It enables the client to create an instance of a service at any time. Essentially that´s the same functionality the new operator provides. A service locator is a factory, and thus it´s just an indirection for instanciating a type.

 

That very fact I think should be made clear when talking about dependency injection and inversion of control. There are two kinds of it: injecting instances and injecting types. Injecting an instance is useful whereever a client just needs one instance of an unknown implementation type. Injecting a type, though, is useful whereever a client needs to be able to create instances of an unknown implementation type at will.

 

Service locator is just one pattern for “injecting” a service type into a client, i.e. making a client independent of concrete implemenations. Types also can be injected through other means; the instance injection patterns described by Martin can be used for type injection, too. Here´s just one example, constructor type injection:

 

class MovieLister

{

    public MovieLister(Type movieFinderType)

    {

        this.finder = (IMovieFinder)Activator.CreateInstance(movieFinderType);

    }

   

 

Instead of passing an instance to the constructor, just the type implementing IMovieFinder is injected. The client then can use the .NET Activator class to instanciate any number of objects of IMovieFinder implemenation. The client can even call CreateInstance() with parameters to be passed on to the constructor of the otherwise unkown type.

 

Setter type injection and interface type injection work accordingly. Just pass in a type instead of an instance of that type. However, there´s a drawback to this kind of type injection: it´s not type-safe. You can pass in any type, not just implementations of IMovieFinder. There´s no compiletime check against it. Fortunately, help´s on its way…

Generics Type Injection or Type Parameter Injection

Now for a type injection specific pattern: generic type injection or type parameter injection.

When you think of constructors as a means to pass objects to an instance during creation, then you can think of generics as a means to pass types to an instance during creation. That suggests using them for type injection:

 

class MovieLister<IMovieFinderClass>

    where IMovieFinderClass : IMovieFinder, new()

{

    private IMovieFinder finder;

 

    public MovieLister()

    {

        this.finder = new IMovieFinderClass();

    }

   

 

Passing a type to an instance by using a generics type parameters solves the problem of the unspecific Type used in the previous type injection example. You can constrain the generics type parameter to match a number of criteria. For example the above MovieLister forces any types injected to really implement the IMovieFinder interface and also provide a default constructor. Any instanciation of a MovieLister not matching these criteria will be flagged at compiletime!

Conclusion

When talking about separating use, specification and implemenation, I mostly use some kind of service locator to “inject” types at runtime into my components. That way of dynamically binding implementations to me simply is close to the way how I would program, would I knew the exact implementation types at development time. But since I just know their specifications (interfaces), I ask the service locator to look up a matching implementation.

 

However, when developing a standalone component for use in multiple, possible yet unknown contexts, I don´t always want to burden it with a service locator framework. The user of the component would need not only to learn my component, but also the service locator framework I chose. In those cases I like to use one of the true injection patterns. And recently I came to like very much generics type injection, because it´s type-safe and allows the client code to instanciate any number of service objects.

5 Comments

  • Yes I completely agree with what you said above.

  • This architecture creates a new dependency on an implementation, because instead of one interface MovieLister you now get N Interfaces MovieLister for each implementation of a finder.

    These Interfaces are not compatible unless you introduce a common abstract base class MovieLister from which MovieLister inherits. But if you do that you get a simple ServiceLocator called MovieLister based on the ServiceLocator (aka. abstract factory) Interface MovieLister which is also a Movie Lister Service.

    Besides that: public MovieLister(IMovieFinder finder)etc. are already type safe at compile time.

    Martin

  • @Martin Lercher: Yes, the MovieLister(IMovieFinder) ctor is already typesafe - but it injects an instance. My point, though, is, not always to inject an instance, but rather just a type.

    As for your "creates a new dependency": In my samples there is no interface for MovieLister. MovieLister is a class.

    However, you´re right, in the end there will be several "internal" types at runtime for each type implementing IMovieFinder used as a type parameter when instanciating a MovieLister. But so what? You already mentioned a remedy.

    Also, I do not propose generics type injection to be a silver bullet to inversion of control problems. It´s just one more pattern to use - and a quite handy one as I find.

    In addition I wanted to point out the difference between instance injection and type injection.

    -Ralf

  • @Martin Lercher: I´ve thought about your comment again and guess, I now know, what you mean by "this architecture creates a new dependency on an implementation". You are critizising my use of a class for MovieLister, right? You wanted to say, the client of movie lister functionality is not decoupled from its implementation.

    That´s of course right. I only used an interface for the movie finder functionality. But that´s only owed to Martin Fowlers sample code which I tried to stay close to. He did not use an interface for the movie lister functionality either.

    In a true component oriented program I of course would hide implementation details for both movie lister and movie finder functionality behind an interface.

    Thx for pointing out this shortcoming of my sample - but it´s just due to keeping the code simple and close to Martin´s.

    -Ralf

  • I recently used a pattern similar to this, which is where I discovered Activator.CreateInstance, and thus you get stronger typing.

    Great article, thanks for writing it!

Comments have been disabled for this content.