Search code examples
c#factory-pattern

Building a better factory


So I am working on a project where I get some data out of a database - there are two pieces of data that make this project nice, one I have the type (they call them events but it essentially just translates to .NET type I created) and then I have XML and I designed the objects so they just deserialize nicely. All this is wonderful, unit tested and all the classes and methods follow the single responsibility principle.

Where my architecture skills get fuzzy is when I am creating the factory to build the business logic to process the .NET objects I have created from the XML.

Basically this is what I have.

  public class EventProcessorFactory : IEventProcessorFactory
    {
        private readonly List<IEventProcessor> _eventProcessors;

        public EventProcessorFactory()
        {
            _eventProcessors = new List<IEventProcessor>();
        }

        public IEventProcessor GetProcessor(Type eventType)
        {
            var typeOfEventProcessor = GetProcessorFromEventType(eventType);
            if (_eventProcessors.Any(x => x.GetType() == typeOfEventProcessor))
                return _eventProcessors.Single(x => x.GetType() == typeOfEventProcessor);
            var processor = BuildProcessorFromType(typeOfEventProcessor);
            _eventProcessors.Add(processor);
            return processor;
        }

        private static Type GetProcessorFromEventType(Type eventType)
        {
            if (eventType == typeof(EnrollmentEventType))
                return typeof(EnrollmentEventProcessor);
            if (eventType == typeof(ClaimantAccountInfoEventType))
                return typeof(ClaimantAccountInfoEventProcessor);
            if (eventType == typeof(PhoneUpdateEventType))
                return typeof(PhoneUpdateEventProcessor);
            if (eventType == typeof(AddressUpdateEventType))
                return typeof(AddressUpdateEventProcessor);
            if (eventType == typeof(ClientAccountInfoEventType))
                return typeof(ClientAccountInfoEventProcessor);
            return null;
        }

        private IEventProcessor BuildProcessorFromType(Type typeOfEventProcessor)
        {
            return ((IEventProcessor)Activator.CreateInstance(typeOfEventProcessor));
        }
    }

So that works but it seems pretty clunky. I have read some articles about using factories but either I didn't read the right one or I am not getting it. I have two problems with the code above.

1) If you add a new event you need to go modify it, I would the later developers to be able to just drop "MyCoolNewEventType" and "MyCoolNewEventProcessor" and not have to modify the method that matches up the event to the processor.

2) I right now when I create an instance am able to just call .CreateInstance(); That's fine cause I don't have any dependencies but the "event processors" are probably going to have dependencies at least on the data base. I am not 100% sure how to handle that, I don't want random calls to Container.Resolve().

If anyone can point in the right direction that would be tremendous.


Solution

  • It sort of depends where you want to store the mappings, but you could write a custom config section:

    public class EventProcessorMapping : ConfigurationElement
    {
    
        [ConfigurationProperty("event", IsRequired = true)]
        public string Event
        {
            get
            {
                return this["event"] as string;
            }
        }
    
        [ConfigurationProperty("processor", IsRequired = true)]
        public string Processor
        {
            get
            {
                return this["processor"] as string;
            }
        }
    }
    
    [ConfigurationCollection(typeof(EventProcessorMapping), CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap)]
    public class EventProcessors : ConfigurationElementCollection
    {
        public EventProcessorMapping this[int index]
        {
            get
            {
                return BaseGet(index) as EventProcessorMapping;
            }
            set
            {
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
                BaseAdd(index, value);
            }
        }
    
        protected override ConfigurationElement CreateNewElement()
        {
            return new EventProcessorMapping();
        }
    
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((EventProcessorMapping)element).Event;
        }
    }
    
    public class RegisterEventProcessorsConfig : ConfigurationSection
    {
    
        public static RegisterEventProcessorsConfig GetConfig()
        {
            return (RegisterEventProcessorsConfig)ConfigurationManager.GetSection("RegisterEventProcessors") ?? new RegisterEventProcessorsConfig();
        }
    
        [ConfigurationProperty("EventProcessors")]
        public EventProcessors EventProcessors
        {
            get
            {
                var o = this["EventProcessors"];
                return o as EventProcessors;
            }
        }
    
    }
    

    Then in your App.config you can have:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>
        <section name="RegisterEventProcessors" type="UnitTestProject1.RegisterEventProcessorsConfig, UnitTestProject1"></section>
      </configSections>
    
      <RegisterEventProcessors>
        <EventProcessors>
          <add event="UnitTestProject1.ClaimantAccountInfoEventType, UnitTestProject1" processor="UnitTestProject1.ClaimantAccountInfoEventProcessor, UnitTestProject1" />
          <add event="UnitTestProject1.EnrollmentEventType, UnitTestProject1" processor="UnitTestProject1.EnrollmentEventProcessor, UnitTestProject1" />
        </EventProcessors>
      </RegisterEventProcessors>
    </configuration>
    

    So, that at least repositions the mapping configuration. As for the factory, if you don't mind the Processor classes being instantiated when the factory is made, you could do:

    public class EventProcessorFactory : IEventProcessorFactory
    {
        private readonly Dictionary<Type, IEventProcessor> _eventProcessors;
    
        public EventProcessorFactory(IEnumerable<EventProcessorMapping> eventProcessorMappings)
        {
            _eventProcessors = new Dictionary<Type, IEventProcessor>();
            foreach (var mapping in eventProcessorMappings)
            {
                AddMapping(Type.GetType(mapping.Event), Type.GetType(mapping.Processor));
            }
        }
    
        public IEventProcessor GetProcessor<T>() where T : IEventType
        {
            return _eventProcessors[typeof(T)];
        }
    
        private void AddMapping(Type eventType, Type processorType)
        {
            var processor = (IEventProcessor)Activator.CreateInstance(processorType);
            _eventProcessors[eventType] = processor;
        }
    }
    

    In the constructor, we are passing in a collection of the mapping config elements, and the processors are created right then and stored in the private collection. Then getting the processor from the factory is basically just a dictionary lookup.

    The two parts come together like this:

    [TestMethod]
    public void TestFactory()
    {
        var config = RegisterEventProcessorsConfig.GetConfig();
        var factory = new EventProcessorFactory(config.EventProcessors.Cast<EventProcessorMapping>());
    
        var processor = factory.GetProcessor<EnrollmentEventType>();
        Assert.IsInstanceOfType(processor, typeof(EnrollmentEventProcessor));
    }