Question

I am trying to learn IOC principle from this screencast Inversion of Control from First Principles - Top Gear Style

I tried do as per screencast but i get an error while AutomaticFactory try create an object of AutoCue. AutoCue class has contructor which takes IClock and not SystemClock. But my question is , in screencast IClock is resolved with SystemClock while inside AutomaticFactory .But in my code , IClock does not get resolved . Am i missing something ?

class Program
{
    static void Main(string[] args)
    {
        //var clarkson = new Clarkson(new AutoCue(new SystemClock()), new Megaphone());
        //var clarkson = ClarksonFactory.SpawnOne();
        var clarkson = (Clarkson)AutomaticFactory.GetOne(typeof(Clarkson));
        clarkson.SaySomething();
        Console.Read();
    }
}
public class AutomaticFactory
{
    public static object GetOne(Type type)
    {
        var constructor = type.GetConstructors().Single();
        var parameters = constructor.GetParameters();

        if (!parameters.Any()) return Activator.CreateInstance(type);

        var args = new List<object>();

        foreach(var parameter  in parameters)
        {
            var arg = GetOne(parameter.ParameterType);
            args.Add(arg);
        }

        var result = Activator.CreateInstance(type, args.ToArray());

        return result;
    }
}

public class Clarkson 
{
    private readonly AutoCue _autocue;
    private readonly Megaphone _megaphone;
    public Clarkson(AutoCue autocue,Megaphone megaphone)
    {
        _autocue = autocue;
        _megaphone =megaphone;
    }
    public void SaySomething()
    {
        var message = _autocue.GetCue();
        _megaphone.Shout(message);
    }
}

public class Megaphone
{
    public void Shout(string message)
    {
        Console.WriteLine(message);
    }
}
public interface IClock
{
    DateTime Now { get; }
}

public class SystemClock : IClock
{
    public DateTime Now { get { return DateTime.Now; } }
}

public class AutoCue
{
    private readonly IClock _clock;

    public AutoCue(IClock clock)
    {
        _clock = clock;
    }
    public string GetCue()
    {
        DateTime now = _clock.Now;
        if (now.DayOfWeek == DayOfWeek.Sunday)
        {
            return "Its a sunday!";
        }
        else
        {
            return "I have to work!";
        }  
    }
}
Was it helpful?

Solution

What you basically implemented is a small IoC container that is able to auto-wire object graphs. But your implementation is only able to create object graphs of concrete objects. This makes your code violate the Dependency Inversion Principle.

What's missing from the implementation is some sort of Register method that tells your AutomaticFactory that when confronted with an abstraction, it should resolve the registered implementation. That could look as follows:

private static readonly Dictionary<Type, Type> registrations = 
    new Dictionary<Type, Type>();

public static void Register<TService, TImplementation>() 
    where TImplementation : class, TService
    where TService : class
{
    registrations.Add(typeof(TService), typeof(TImplementation));
}

No you will have to do an adjustment to the GetOne method as well. You can add the following code at the start of the GetOne method:

    if (registrations.ContainsKey(type))
    {
        type = registrations[type];
    }

That will ensure that if the supplied type is registered in the AutomaticFactory as TService, the mapped TImplementation will be used and the factory will continue using this implementation as the type to build up.

This does mean however that you now have to explicitly register the mapping between IClock and SystemClock (which is a quite natural thing to do if you're working with an IoC container). You must make this mapping before the first instance is resolved from the AutomaticFactory. So you should add the following line to to the beginning of the Main method:

AutomaticFactory.Register<IClock, SystemClock>();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top