3

所以我正在做一个项目,我从数据库中获取一些数据 - 有两个数据使这个项目很好,一个我有类型(他们称它们为事件,但它本质上只是转换为我创建的 .NET 类型) 然后我有了 XML,我设计了对象,所以它们可以很好地反序列化。这一切都很棒,经过单元测试,所有的类和方法都遵循单一职责原则。

当我创建工厂以构建业务逻辑以处理我从 XML 创建的 .NET 对象时,我的架构技能变得模糊。

基本上这就是我所拥有的。

  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));
        }
    }

所以这行得通,但它看起来很笨重。我已经阅读了一些关于使用工厂的文章,但要么我没有阅读正确的文章,要么我没有得到它。上面的代码有两个问题。

1)如果您添加了一个新事件,您需要对其进行修改,我希望以后的开发人员能够只删除“MyCoolNewEventType”和“MyCoolNewEventProcessor”,而不必修改将事件匹配到处理器的方法。

2)我现在在创建实例时可以调用 .CreateInstance(); 这很好,因为我没有任何依赖关系,但“事件处理器”可能至少会依赖数据库。我不是 100% 确定如何处理,我不想随机调用 Container.Resolve()。

如果有人能指出正确的方向,那将是巨大的。

4

4 回答 4

4

您正在寻找的是可配置的工厂。它可以使用依赖注入框架或最简单的方法来完成 - 外部配置(想到 xml 文件),它将存储您可能实现的列表,并将按需加载/修改。

使用此解决方案,您将拥有类似的东西

<Processors>
  <Processor dllname='' classname=''>
   ....
</Processors>

并且您必须通过使用反射/.net 为您提供的任何 xml 读取技术来加载它。

于 2012-10-11T17:22:01.650 回答
3

您可以使用反射并在这些属性中设置处理器类型。

[ProcessorType(typeof(EnrollmentEventProcessor)]
class EnrollmentEvent { ... }

简单干净的解决方案。对于创建的类型,我建议使用 aDictionary<Type, Object>来加快访问速度。

于 2012-10-11T17:35:49.433 回答
1

这有点取决于您要存储映射的位置,但您可以编写自定义配置部分:

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;
        }
    }

}

然后在你的App.config你可以有:

<?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>

因此,这至少重新定位了映射配置。至于工厂,如果您不介意在制造工厂时实例化处理器类,您可以这样做:

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;
    }
}

在构造函数中,我们传入映射配置元素的集合,然后立即创建处理器并将其存储在私有集合中。然后从工厂获取处理器基本上只是一个字典查找。

这两部分像这样结合在一起:

[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));
}
于 2012-10-12T02:11:20.820 回答
1

对于这种情况,我肯定会在 DI 中使用命名工厂,但我没有 Unity 经验(我自己是 Autofac 人)。但我有点惊讶没有人提出命名约定方法:让 EventType 的名称决定使用什么处理器。这将不需要额外的代码,因为添加了新的事件类型和处理器,只要处理器的名称遵循约定。

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;
}

如果类型和处理器位于不同的程序集中,则从名称中获取解析类型的Type.GetType方法会变得更加困难,这就是我为此使用 DI 的主要原因。

于 2012-10-12T07:37:07.887 回答