Reputation: 2769
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.
Upvotes: 3
Views: 527
Reputation: 2233
I would definitely used a named factory in the DI for this scenario, but I have no experience in Unity (I'm an Autofac guy myself). But I'm a little surprised no one has proposed the naming-convention approach: Let the name of the EventType decide what processor to use. This will require no additional code as new event types and processors are added, as long as the name of the processor follows the convention.
private static Type GetProcessorFromEventType(Type eventType)
{
// Assuming phrase "Type" only is present at the end of the EventType name
var processorName = eventType.Name.Replace("Type", "Processor");
var processorType = Type.GetType(processorName);
return processorType;
}
Getting the Type.GetType
method to resolve type from name gets more difficult if types and processors are in different assemblies, that's the primary reason I would use the DI for this.
Upvotes: 1
Reputation: 43748
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));
}
Upvotes: 1
Reputation: 6281
You could use reflection and set the processor-type in theese attributes.
[ProcessorType(typeof(EnrollmentEventProcessor)]
class EnrollmentEvent { ... }
Easy and clean solution. And for the created types i would advice to use a Dictionary<Type, Object>
to speed up the access.
Upvotes: 3
Reputation: 2123
What you're looking for is configurable factory. it can be done with dependency injection frameworks, or the easiest way - an external configuration (xml file comes to mind), which will store the list of ur possible implementations and will be loaded/modified on demand.
With this solution you will have something like
<Processors>
<Processor dllname='' classname=''>
....
</Processors>
and you will have to load it, by using reflection / any xml reading technique .net provides you with.
Upvotes: 4