Dynamic factory implementation using .NET reflection

Posted by Max | Posted in programming | Posted on 04-12-2009

0

The following example uses dynamic factory which returns formatter object for given type using the custom attribite specifying which type is handled by formatter. The factory uses .NET reflection mechamism, rather than a series of “if” or “switch” statements, so the factory class itself is not updated when new formatters added into the solution.


using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            var list = new List<object>
            {
                2.45m,
                DateTime.Now,
                6m,
                DateTime.Now.AddDays(2)
            };

            foreach (var item in list)
            {
                var formatter = FormatterFactory.GetFormatter(item.GetType());
                Console.WriteLine(formatter.FormattedValue(item));
            }
            Console.ReadLine();
        }
    }

    /// <summary>
    /// Common interface used by formatters
    /// </summary>
    public interface IFormatter
    {
        string FormattedValue(object value);
    }

    /// <summary>
    /// This attrubute sets the type is handled by formatter
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class HandlerForTypeAttribute : Attribute
    {
        public Type Type { get; private set; }
        public HandlerForTypeAttribute(Type type) { Type = type; }
    }

    public static class FormatterFactory
    {
        private static Dictionary<Type, Type> _typeMapping;

        /// <summary>
        /// Returns a formatter class instance created by using reflection
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static IFormatter GetFormatter(Type type)
        {
            if (TypeMapping.ContainsKey(type))
                return (IFormatter)Activator.CreateInstance(TypeMapping[type]);
            throw new ArgumentException("Not supported type");
        }

        /// <summary>
        /// Returns a list of derived classes for given type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static IList<Type> GetDerivedClasses(Type type)
        {
            return (AppDomain.CurrentDomain.GetAssemblies().ToList()
                .SelectMany(s => s.GetTypes())
                .Where(type.IsAssignableFrom)).ToList();
        }

        /// <summary>
        /// Cached dictionary of mappings between types and their formatters
        /// </summary>
        private static Dictionary<Type, Type> TypeMapping
        {
            get
            {
                if (_typeMapping == null)
                {
                    _typeMapping = new Dictionary<Type, Type>();

                    // add all classes implementing IFormatter
                    // interface and having HandlerForType attribute
                    // into type mapping dictionary
                    foreach (Type type in GetDerivedClasses(typeof(IFormatter)))
                    {
                        var typeAttr = (HandlerForTypeAttribute)Attribute.GetCustomAttribute(
                            type, typeof(HandlerForTypeAttribute)
                        );
                        if (typeAttr != null)
                            _typeMapping.Add(typeAttr.Type, type);
                    }
                }
                return _typeMapping;
            }
        }
    }

    /// <summary>
    /// Formatter for decimal type
    /// </summary>
    [HandlerForType(typeof(decimal))]
    public class DecimalFormatter : IFormatter
    {
        public string FormattedValue(object value)
        {
            return String.Format("Integer value {0:#,###.00}", value);
        }
    }

    /// <summary>
    /// Formatter for DateTime type
    /// </summary>
    [HandlerForType(typeof(DateTime))]
    public class DateTimeFormatter : IFormatter
    {
        public string FormattedValue(object value)
        {
            return String.Format("DateTime value {0:d}", value);
        }
    }

    // More formatters implementing IFormatter could be added here,
    // without the need to change FormatterFactory class
}

Console output:
console_output

Write a comment