Delegates as type aliases

In this post I will walk through how to refactor a Factory, moving from a sequence of ifs to a dictionary implementation, and using delegates as a type alias for my object creation methods.

Here is a naive implementation of the Factory. You can see the series of if statements, and the direct construction and returning of the created object from the body of each:

public class MessageFactory : IMessageFactory
{
    private readonly IArtifactApi _artifactApi;
    private readonly ITarballReader _tarballReader;

    public MessageFactory(IArtifactApi artifactApi, ITarballReader tarballReader)
    {
        _artifactApi = artifactApi;
        _tarballReader = tarballReader;
    }

    public IMessage Create(string id, DateTime timestamp, string fileType, Payload payload)
    {
        if (fileType == FileTypes.AnalyticInstrumentsXml)
        {
            return new XmlMessage(_artifactApi, id, timestamp, payload);
        }
        if (fileType == FileTypes.BrokersXml)
        {
            return new XmlMessage(_artifactApi, id, timestamp, payload);
        }
        if (fileType == FileTypes.EconomyXml)
        {
            return new XmlMessage(_artifactApi, id, timestamp, payload);
        }
        if (fileType == FileTypes.OrdersXml)
        {
            return new XmlMessage(_artifactApi, id, timestamp, payload);
        }
        if (fileType == FileTypes.UsersXml)
        {
            return new XmlMessage(_artifactApi, id, timestamp, payload);
        }
        if (fileType == FileTypes.TradeTarball)
        {
            return new TarballMessage(_artifactApi, _tarballReader, id, timestamp, payload);
        }
        throw new NotImplementedException();
    }
}

I want to separate my conditional logic from my object construction, so I Extract Method for each of the constructor invocations:

public class MessageFactory : IMessageFactory
{
	private readonly IArtifactApi _artifactApi;
	private readonly ITarballReader _tarballReader;

	public MessageFactory(IArtifactApi artifactApi, ITarballReader tarballReader)
	{
		_artifactApi = artifactApi;
		_tarballReader = tarballReader;
	}

	public IMessage Create(string id, DateTime timestamp, string fileType, Payload payload)
	{
		if (fileType == FileTypes.AnalyticInstrumentsXml)
		{
			return CreateXmlMessage(_artifactApi, id, timestamp, payload);
		}
		if (fileType == FileTypes.BrokersXml)
		{
			return CreateXmlMessage(_artifactApi, id, timestamp, payload);
		}
		if (fileType == FileTypes.EconomyXml)
		{
			return CreateXmlMessage(_artifactApi, id, timestamp, payload);
		}
		if (fileType == FileTypes.OrdersXml)
		{
			return CreateXmlMessage(_artifactApi, id, timestamp, payload);
		}
		if (fileType == FileTypes.UsersXml)
		{
			return CreateXmlMessage(_artifactApi, id, timestamp, payload);
		}
		if (fileType == FileTypes.TradeTarball)
		{
			return CreateTarballMessage(_artifactApi, _tarballReader, id, timestamp, payload);
		}
		throw new NotImplementedException();
	}

	private static IMessage CreateXmlMessage(IArtifactApi artifactApi, string id, DateTime timestamp, Payload payload)
	{
		return new XmlMessage(artifactApi, id, timestamp, payload);
	}

	private static IMessage CreateTarballMessage(IArtifactApi artifactApi, ITarballReader tarballReader, string id, DateTime timestamp, Payload payload)
	{
		return new TarballMessage(artifactApi, tarballReader, id, timestamp, payload);
	}
}

I want to convert my chain of conditionals into a Dictionary, but to do so, I need my methods to have the same type signature. I add a dummy parameter to CreateXmlMessage:

public class MessageFactory : IMessageFactory
{
	private readonly IArtifactApi _artifactApi;
	private readonly ITarballReader _tarballReader;

	public MessageFactory(IArtifactApi artifactApi, ITarballReader tarballReader)
	{
		_artifactApi = artifactApi;
		_tarballReader = tarballReader;
	}

	public IMessage Create(string id, DateTime timestamp, string fileType, Payload payload)
	{
		if (fileType == FileTypes.AnalyticInstrumentsXml)
		{
			return CreateXmlMessage(_artifactApi, _tarballReader, id, timestamp, payload);
		}
		if (fileType == FileTypes.BrokersXml)
		{
			return CreateXmlMessage(_artifactApi, _tarballReader, id, timestamp, payload);
		}
		if (fileType == FileTypes.EconomyXml)
		{
			return CreateXmlMessage(_artifactApi, _tarballReader, id, timestamp, payload);
		}
		if (fileType == FileTypes.OrdersXml)
		{
			return CreateXmlMessage(_artifactApi, _tarballReader, id, timestamp, payload);
		}
		if (fileType == FileTypes.UsersXml)
		{
			return CreateXmlMessage(_artifactApi, _tarballReader, id, timestamp, payload);
		}
		if (fileType == FileTypes.TradeTarball)
		{
			return CreateTarballMessage(_artifactApi, _tarballReader, id, timestamp, payload);
		}
		throw new NotImplementedException();
	}

	private static IMessage CreateXmlMessage(IArtifactApi artifactApi, ITarballReader tarballReader, string id, DateTime timestamp, Payload payload)
	{
		return new XmlMessage(artifactApi, id, timestamp, payload);
	}

	private static IMessage CreateTarballMessage(IArtifactApi artifactApi, ITarballReader tarballReader, string id, DateTime timestamp, Payload payload)
	{
		return new TarballMessage(artifactApi, tarballReader, id, timestamp, payload);
	}
}

At this point, I can create a Dictionary to map from fileType to my creation methods:

public class MessageFactory : IMessageFactory
{
	private readonly IArtifactApi _artifactApi;
	private readonly ITarballReader _tarballReader;

	private readonly Dictionary<string, Func<IArtifactApi, ITarballReader, string, DateTime, Payload, IMessage>> _factories =
		new Dictionary<string, Func<IArtifactApi, ITarballReader, string, DateTime, Payload, IMessage>>
		{
			{FileTypes.AnalyticInstrumentsXml, CreateXmlMessage},
			{FileTypes.BrokersXml, CreateXmlMessage},
			{FileTypes.EconomyXml, CreateXmlMessage},
			{FileTypes.OrdersXml, CreateXmlMessage},
			{FileTypes.UsersXml, CreateXmlMessage},
			{FileTypes.TradeTarball, CreateTarballMessage}
		};

	public MessageFactory(IArtifactApi artifactApi, ITarballReader tarballReader)
	{
		_artifactApi = artifactApi;
		_tarballReader = tarballReader;
	}

	public IMessage Create(string id, DateTime timestamp, string fileType, Payload payload)
	{
		return _factories[fileType](_artifactApi, _tarballReader, id, timestamp, payload);
	}

	private static IMessage CreateXmlMessage(IArtifactApi artifactApi, ITarballReader tarballReader, string id, DateTime timestamp, Payload payload)
	{
		return new XmlMessage(artifactApi, id, timestamp, payload);
	}

	private static IMessage CreateTarballMessage(IArtifactApi artifactApi, ITarballReader tarballReader, string id, DateTime timestamp, Payload payload)
	{
		return new TarballMessage(artifactApi, tarballReader, id, timestamp, payload);
	}
}

This is looking nicer, except for the type signature of the Dictionary, which is fairly gruesome:

private readonly Dictionary<string, Func<IArtifactApi, ITarballReader, string, DateTime, Payload, IMessage>> _factories =
		new Dictionary<string, Func<IArtifactApi, ITarballReader, string, DateTime, Payload, IMessage>> {/*...*/};

Now, later in my refactoring I would want to remove the primitive obsession around fileType by using either an enum or, preferably, a class with behaviour. But at the moment I’m focusing on internally refactoring this Factory.

I would also like to remove the data clumps passed into the creation functions. Again, I might find an appropriate type to contain the data, and this might lead me to a more polymorphous implementation.

But right now, I want to make my code a little more succinct and semantically clearer, so I use a delegate as a type alias for Func<IArtifactApi, ITarballReader, string, DateTime, Payload, IMessage>:

public class MessageFactory : IMessageFactory
{
	private readonly IArtifactApi _artifactApi;
	private readonly ITarballReader _tarballReader;

	private readonly Dictionary<string, CreateMessage> _factories =
		new Dictionary<string, CreateMessage>
		{
			{FileTypes.AnalyticInstrumentsXml, CreateXmlMessage},
			{FileTypes.BrokersXml, CreateXmlMessage},
			{FileTypes.EconomyXml, CreateXmlMessage},
			{FileTypes.OrdersXml, CreateXmlMessage},
			{FileTypes.UsersXml, CreateXmlMessage},
			{FileTypes.TradeTarball, CreateTarballMessage}
		};

	public MessageFactory(IArtifactApi artifactApi, ITarballReader tarballReader)
	{
		_artifactApi = artifactApi;
		_tarballReader = tarballReader;
	}

	public IMessage Create(string id, DateTime timestamp, string fileType, Payload payload)
	{
		return _factories[fileType](_artifactApi, _tarballReader, id, timestamp, payload);
	}

	private static IMessage CreateXmlMessage(IArtifactApi artifactApi, ITarballReader tarballReader, string id, DateTime timestamp, Payload payload)
	{
		return new XmlMessage(artifactApi, id, timestamp, payload);
	}

	private static IMessage CreateTarballMessage(IArtifactApi artifactApi, ITarballReader tarballReader, string id, DateTime timestamp, Payload payload)
	{
		return new TarballMessage(artifactApi, tarballReader, id, timestamp, payload);
	}

	private delegate IMessage CreateMessage(IArtifactApi artifactApi, ITarballReader tarballReader, string id,DateTime timestamp, Payload payload);
}

It is interesting that neither of the methods explicitly implements this delegate: it’s sufficient for the signatures to coincide. This is one of those areas of C# where functional programming patterns creep into an otherwise fairly object-oriented idiom (type aliases or abbreviations are commonplace in F#, for example). I would be wary of exposing this delegate outside the class in which it is declared, and would choose a more object-oriented approach if I wanted to extract different creation behaviours, but I like the succinctness and clarity that this technique gives me within a single class.


Updated onĀ 24 October 2017: I had omitted the new parameter from the method invocations in the third code sample. This is now fixed.

2 thoughts on “Delegates as type aliases

  1. Hi Matthew,

    Are you the same chap who did the three you tube refactoring videos foe Codurance? If so, they are awesome. Are you going to do any more?

    1. Yes, that’s me. I’m no longer at Codurance, and I’ve been rather busy with other things recently (hence also my late response: sorry!), but I’m thrilled to hear you enjoyed them, and I’ll certainly take this as a hint I should record some more!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s