Using Dictionaries to replace long if or switch statements


Imagine the following scenario – you have created some validators for your models: Foo, Bar and Xyz.

    public interface IValidator
    {
        bool Validate(object model);
    }

    // Validator for Foo class
    public class FooValidator : IValidator
    {
        public bool Validate(object model)
        {
            // ...
        }
    }

    // Validator for Bar class
    public class BarValidator : IValidator
    {
        public bool Validate(object model)
        {
            // ...
        }
    }

    // Validator for Xyz class
    public class XyzValidator : IValidator
    {
        public bool Validate(object model)
        {
            // ...
        }
    }

You decide to create a factory class for the validators to prevent users from creating instances directly using the new keyword. For example, this code would create a validator for Foo:

    var model = new Foo();
    var validatorFactory = new ValidatorFactory();
    IValidator validator = validatorFactory.CreateValidatorFor<Foo>();

    if (validator.Validate(model))
    {
        // ...
    }

Implementation of the factory:

    using System;

    public class ValidatorFactory
    {
        public IValidator CreateValidatorFor<T>()
        {
            Type modelType = typeof (T);
            IValidator validator = CreateValidatorFor(modelType);

            return validator;
        }

        public IValidator CreateValidatorFor(Type modelType)
        {
            if (modelType == null)
            {
                throw new ArgumentNullException("modelType");
            }

            if(modelType == typeof(Foo))
            {
                return new FooValidator();
            }
            else if(modelType == typeof(Bar))
            {
                return new BarValidator();
            }
            else if(modelType == typeof(Xyz))
            {
                return new XyzValidator();
            }

            string errorMessage = string.Concat("Could not find validator for type ", modelType.FullName);
            throw new ArgumentException("modelType", errorMessage);
        }
    }

As you can see, for each model you have an if statement. This works fine if you only need to create 2 or 3 validators but if you need more your code will get bigger and bigger and will be harder to read/maintain. The same applies to switch statements.

The first step to solve this problem is to create a Dictionary to store the validators. The key of the Dictionary will be the type of the model and the value will be a delegate that creates an instance of the validator for that model:

	var validators = new Dictionary<Type, Func<IValidator>>
	{
		{ typeof(Foo), () => new FooValidator() },
		{ typeof(Bar), () => new BarValidator() },
		{ typeof(Xyz), () => new XyzValidator() }
	};

Changing the implementation of the factory to use the dictionary:

    public class ValidatorFactory : IValidatorFactory
    {
        private static Dictionary<Type, Func<IValidator>> _validators =
			new Dictionary<Type, Func<IValidator>> {
            { typeof(Foo), () => new FooValidator() },
            { typeof(Bar), () => new BarValidator() },
            { typeof(Xyz), () => new XyzValidator() }
        };

        public IValidator CreateValidatorFor<T>()
        {
            Type modelType = typeof (T);
            IValidator validator = CreateValidatorFor(modelType);

            return validator;
        }

        public IValidator CreateValidatorFor(Type modelType)
        {
            if (modelType == null)
            {
                throw new ArgumentNullException("modelType");
            }

            Func<IValidator> validatorFunc;

            if (_validators.TryGetValue(modelType, out validatorFunc))
            {
                IValidator validator = validatorFunc();

                return validator;
            }

            string errorMessage = string.Concat("Could not find validator for type ", modelType.FullName);
            throw new ArgumentException("modelType", errorMessage);
        }
    }

That’s it, code looks much nicer now! To configure a new validator just add a new key/value pair to the _validators Dictionary.

Finally, testing the code using NUnit:

	// arrange
	var factory = new ValidatorFactory();

	// act
	IValidator validator = factory.CreateValidatorFor<Foo>();

	// assert
	Assert.That(validator, Is.TypeOf<FooValidator>());
Advertisements

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s