Programming  /  Architecture November 09, 2010

Inversion of Control – It’s broader than just injecting components

I have previously written about Inversion of Control in .NET. There I focused on making a class open for extension by being able to replace the components that it uses. I demonstrated two techniques for doing that, Dependency Injection and Service Location.

Today I attended an excellent workshop about Scala development held by Ted Neward at Øredev. During the session Ted demonstrated a different, functional, aspect of the Inversion of Control concept which he illustrated in Scala. Since it’s fully possible to do the same thing in C# I thought would borrow and modify his example to show a different type of Inversion of Control compared to what I did in my previous article.

Injecting functions

Let’s look at a very simple example. It’s rather silly, but I think it illustrates the point quite well so humor me :)

We have a class named Car with a single property, Make. The class overrides the ToString method to return its make.

class Car
{
    public Car(string make)
    {
        Make = make;
    }

    public string Make { get; private set; }

    public override string ToString()
    {
        return string.Format("I'm a {0}", Make);
    }
}

We also have a console application that creates a couple of cars and then iterates over them printing the result of their ToString methods to the console.

var cars = new List<Car>
    {
        new Car("Volvo"),
        new Car("Saab")
    };

foreach (var car in cars)
    Console.WriteLine(car);

Now, let’s say that we also wanted to add a Ferrari and that we wanted our Ferrari’s ToString method to return a different result than the others. Had we been using a dynamic language such as Ruby we could have just switched that specific objects implementation of the ToString method out for something else. But in C# we have no means of doing that. We could of course create a subclass of Car and override the method but that might not always be a good, or even an impossible, option.

However we can quite easily make it possible to change the implementation of the ToString method of all Cars by making it delegate to another function which we in turn make possible to replace utilizing the fact that we can pass functions around as values in C#.

We begin by creating a field for the function in the Car class and make its ToString method delegate to that.

private Func<string> toStringImplementation;

public override string ToString()
{
    return toStringImplementation();
}

Of course with just this change our program will crash and burn as the field will be null for our existing cars. We therefore add a default implementation in the constructor with the same functionality as the old ToString method.

public Car(string make)
{
    Make = make;
    toStringImplementation = 
        () => { return string.Format("I'm a {0}", Make); };
}

Finally we add a way to change which implementation is used.

public void SetToStringImplementation(Func<string> implementation)
{
    toStringImplementation = implementation;
}

We are now able to change the implementation of the ToString method of specific instances of the Car class at runtime, enabling us to create our Ferrari without creating a subclass of Car.

var cars = new List<Car>
    {
        new Car("Volvo"),
        new Car("Saab")
    };

var ferrari = new Car("Ferrari");
ferrari.SetToStringImplementation(() => "I'm more than a car, I'm a lifestyle");
cars.Add(ferrari);

foreach (var car in cars)
    Console.WriteLine(car);

This is as I mentioned before a rather silly example but I do think it illustrates a different, and interesting, aspect of Inversion of Control compared to my previous post. Hopefully it also illustrates that the concept of Inversion of Control is much broader than just dependency injection.

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Joel Abrahamsson

Joel Abrahamsson

I'm a passionate web developer and systems architect living in Stockholm, Sweden. I work as CTO for a large media site and enjoy developing with all technologies, especially .NET, Node.js, and ElasticSearch. Read more

Comments

comments powered by Disqus

More about Software Architecture