使用 Ninject 注入工厂字典
Injecting a dictionary of factories with Ninject
我知道如何通过构造函数注入将一个或一组依赖接口实例注入 class。但是,在我目前的情况下,我的任务有点不同。
我有几个 class,每个都有关联的 "Processor" class。这些处理器正在实现相同的 IProcessor
接口,一个通用的 Processor
class 将处理一组对象,为每个对象使用适当的处理器。为一种类型创建处理器可能很昂贵,因此我使用工厂并仅在需要时实例化处理器。
代码看起来像这样。
public interface IProcessor {
void Process(object item);
}
public class Processor {
private readonly Dictionary<Type, Func<IProcessor>> _processors;
public Processor(IDictionary<Type, Func<IProcessor>> processors) {
_processors = processors;
}
public void Process(IEnumerable items) {
foreach (var item in items) {
var processorFactory = _processors.GetValueOrDefault(item.GetType());
if (processorFactory == null) continue; // for simplicity
var processor = processorFactory();
processor.Process(item);
}
}
}
如何在 Ninject 中为此注册绑定?或者是否有任何类型的替代模式更 "DI friendly"?
我想在应用程序入口点级别配置这些 "processor bindings"。
另一种方法是在 Processor
class 中有一个处理器工厂的静态字典,并在入口点手动注册绑定,但我想避免使用静态依赖项。还是在这种特殊情况下会更好?
更新
我想到的另一种混合替代方案是这样的。我会在 Processor
class 中有一个静态 Factories
字典。在那里我可以将基本的默认实现作为外观。
然后在我的 Ninject 模块中我可以写这样的东西。
public class MyModule : NinjectModule
{
public override void Load()
{
// ... my "standard" bindings
Processor.Factories[typeof(MyItem1)] = () => Kernel.Get<MyItem1Processor>();
Processor.Factories[typeof(MyItem2)] = () => Kernel.Get<MyItem2Processor>();
}
}
我知道我在这里使用的是 "evil" 静态的东西,但仍然可以很容易地以一种易于阅读的方式使用 DI,利用 Kernel
属性模块。
在Load
方法中使用模块的Kernel
属性安全吗?例如,我的意思是一个模块可以加载到更多内核中吗?
如有任何想法,我们将不胜感激。
如果你想要惰性初始化,工厂 class 而不是 Func
怎么样?
拥有基地工厂class:
public abstract class ProcessorFactory
{
public abstract Type ItemType { get; }
public abstract IProcessor GetProcessor();
}
为每个项目类型创建一个 class 的具体实例,并将这些实例的集合注入到您的构造函数中。然后从中构建你的字典:
public class Processor
{
private readonly Dictionary<Type, ProcessorFactory> _processors;
public Processor(IEnumerable<ProcessorFactory> processors)
{
_processors = processors.ToDictionary<ProcessorFactory, Type>(p => p.ItemType);
}
public void Process(IEnumerable items)
{
foreach (var item in items)
{
var processorFactory = _processors.GetValueOrDefault(item.GetType());
if (processorFactory == null) continue; // for simplicity
var processor = processorFactory.GetProcessor();
processor.Process(item);
}
}
}
更新 1
下面是完整工厂实现的示例代码:
首先我把工厂改成了接口:
public interface IProcessorFactory
{
Type ItemType { get; }
IProcessor GetProcessor();
}
然后我为工厂创建了一个抽象通用基础class:
public abstract class ProcessorFactoryBase<TItem> : IProcessorFactory
{
private Lazy<IProcessor> _factory;
public ProcessorFactoryBase(Func<IProcessor> factory)
{
_factory = new Lazy<IProcessor>(factory);
}
public Type ItemType
{
get { return typeof(TItem); }
}
public IProcessor GetProcessor()
{
return _factory.Value;
}
}
要创建工厂,只需从具有适当项目类型的基础继承并实现构造函数:
public class ProcessorFactoryA : ProcessorFactoryBase<ItemA>
{
public ProcessorFactoryA(Func<IProcessor> factory) : base(factory) { }
}
请注意,工厂 class 与项目类型相关;处理器类型通过绑定注入:
public class Bindings : NinjectModule
{
public override void Load()
{
Bind<IProcessorFactory>().ToMethod(context => new ProcessorFactoryA(() => context.Kernel.Get<ProcessorX>()));
Bind<IProcessorFactory>().ToMethod(context => new ProcessorFactoryB(() => context.Kernel.Get<ProcessorY>()));
Bind<IProcessorFactory>().ToMethod(context => new ProcessorFactoryC(() => context.Kernel.Get<ProcessorZ>()));
// Note that item type D is handled by processor X
Bind<IProcessorFactory>().ToMethod(context => new ProcessorFactoryD(() => context.Kernel.Get<ProcessorX>()));
}
}
我用完整的工作代码制作了一个 .NET fiddle:http://dotnetfiddle.net/aD9E2y。
当您尝试 运行 fiddle 时出现错误,但您可以将代码抓取到 .NET 控制台项目中,然后 运行s。
有些人不喜欢它们,但我使用 T4 模板来做一些事情,比如使用反射自动生成处理器工厂 classes。但是,仍然需要手动创建绑定,因为无法推断项目类型和处理器之间的关联。
我正在用我的最终解决方案回答我的问题。
我相信,在软件开发过程中,如果有什么东西 "doesn't want to be put together",那么它就是某种气味的迹象,大多数时候我需要返回几个级别才能找到它。这里也有类似的东西。
我意识到在这种情况下使用工厂模式并不是一个好的设计,因为:
- 我认为实例化一个处理器对象永远不会如此昂贵,因为每个处理器对象都应该优化它们的资源以仅在调用
Process
时使用它们。
- 即使实例化很昂贵,使用我的原始模式,只要处理列表中有合适的对象,就会创建一个新实例。 (这可以由处理器处理,但仍然看起来一点也不好。)
- 无法根据优先级添加自定义处理器。假设
ProcessorA
处理 ClassA
,ClassB
扩展 ClassA
和 ProcessorB
处理 ClassB
。我无法阻止 ProcessorA
处理 ClassB
并允许其他(非自定义处理的)ClassA
后代仍然与 ProcessorA
同时处理。这是因为 Dictionary
结构。
所以我决定简化实现,把一个IProcessor
的可枚举直接传给主处理器,在IProcessor
中有一个CanProcess(object obj)
。这样我就可以直接使用任何 DI 容器来注入所有绑定实现的列表。
我知道如何通过构造函数注入将一个或一组依赖接口实例注入 class。但是,在我目前的情况下,我的任务有点不同。
我有几个 class,每个都有关联的 "Processor" class。这些处理器正在实现相同的 IProcessor
接口,一个通用的 Processor
class 将处理一组对象,为每个对象使用适当的处理器。为一种类型创建处理器可能很昂贵,因此我使用工厂并仅在需要时实例化处理器。
代码看起来像这样。
public interface IProcessor {
void Process(object item);
}
public class Processor {
private readonly Dictionary<Type, Func<IProcessor>> _processors;
public Processor(IDictionary<Type, Func<IProcessor>> processors) {
_processors = processors;
}
public void Process(IEnumerable items) {
foreach (var item in items) {
var processorFactory = _processors.GetValueOrDefault(item.GetType());
if (processorFactory == null) continue; // for simplicity
var processor = processorFactory();
processor.Process(item);
}
}
}
如何在 Ninject 中为此注册绑定?或者是否有任何类型的替代模式更 "DI friendly"?
我想在应用程序入口点级别配置这些 "processor bindings"。
另一种方法是在 Processor
class 中有一个处理器工厂的静态字典,并在入口点手动注册绑定,但我想避免使用静态依赖项。还是在这种特殊情况下会更好?
更新
我想到的另一种混合替代方案是这样的。我会在 Processor
class 中有一个静态 Factories
字典。在那里我可以将基本的默认实现作为外观。
然后在我的 Ninject 模块中我可以写这样的东西。
public class MyModule : NinjectModule
{
public override void Load()
{
// ... my "standard" bindings
Processor.Factories[typeof(MyItem1)] = () => Kernel.Get<MyItem1Processor>();
Processor.Factories[typeof(MyItem2)] = () => Kernel.Get<MyItem2Processor>();
}
}
我知道我在这里使用的是 "evil" 静态的东西,但仍然可以很容易地以一种易于阅读的方式使用 DI,利用 Kernel
属性模块。
在Load
方法中使用模块的Kernel
属性安全吗?例如,我的意思是一个模块可以加载到更多内核中吗?
如有任何想法,我们将不胜感激。
如果你想要惰性初始化,工厂 class 而不是 Func
怎么样?
拥有基地工厂class:
public abstract class ProcessorFactory
{
public abstract Type ItemType { get; }
public abstract IProcessor GetProcessor();
}
为每个项目类型创建一个 class 的具体实例,并将这些实例的集合注入到您的构造函数中。然后从中构建你的字典:
public class Processor
{
private readonly Dictionary<Type, ProcessorFactory> _processors;
public Processor(IEnumerable<ProcessorFactory> processors)
{
_processors = processors.ToDictionary<ProcessorFactory, Type>(p => p.ItemType);
}
public void Process(IEnumerable items)
{
foreach (var item in items)
{
var processorFactory = _processors.GetValueOrDefault(item.GetType());
if (processorFactory == null) continue; // for simplicity
var processor = processorFactory.GetProcessor();
processor.Process(item);
}
}
}
更新 1
下面是完整工厂实现的示例代码:
首先我把工厂改成了接口:
public interface IProcessorFactory
{
Type ItemType { get; }
IProcessor GetProcessor();
}
然后我为工厂创建了一个抽象通用基础class:
public abstract class ProcessorFactoryBase<TItem> : IProcessorFactory
{
private Lazy<IProcessor> _factory;
public ProcessorFactoryBase(Func<IProcessor> factory)
{
_factory = new Lazy<IProcessor>(factory);
}
public Type ItemType
{
get { return typeof(TItem); }
}
public IProcessor GetProcessor()
{
return _factory.Value;
}
}
要创建工厂,只需从具有适当项目类型的基础继承并实现构造函数:
public class ProcessorFactoryA : ProcessorFactoryBase<ItemA>
{
public ProcessorFactoryA(Func<IProcessor> factory) : base(factory) { }
}
请注意,工厂 class 与项目类型相关;处理器类型通过绑定注入:
public class Bindings : NinjectModule
{
public override void Load()
{
Bind<IProcessorFactory>().ToMethod(context => new ProcessorFactoryA(() => context.Kernel.Get<ProcessorX>()));
Bind<IProcessorFactory>().ToMethod(context => new ProcessorFactoryB(() => context.Kernel.Get<ProcessorY>()));
Bind<IProcessorFactory>().ToMethod(context => new ProcessorFactoryC(() => context.Kernel.Get<ProcessorZ>()));
// Note that item type D is handled by processor X
Bind<IProcessorFactory>().ToMethod(context => new ProcessorFactoryD(() => context.Kernel.Get<ProcessorX>()));
}
}
我用完整的工作代码制作了一个 .NET fiddle:http://dotnetfiddle.net/aD9E2y。
当您尝试 运行 fiddle 时出现错误,但您可以将代码抓取到 .NET 控制台项目中,然后 运行s。
有些人不喜欢它们,但我使用 T4 模板来做一些事情,比如使用反射自动生成处理器工厂 classes。但是,仍然需要手动创建绑定,因为无法推断项目类型和处理器之间的关联。
我正在用我的最终解决方案回答我的问题。
我相信,在软件开发过程中,如果有什么东西 "doesn't want to be put together",那么它就是某种气味的迹象,大多数时候我需要返回几个级别才能找到它。这里也有类似的东西。
我意识到在这种情况下使用工厂模式并不是一个好的设计,因为:
- 我认为实例化一个处理器对象永远不会如此昂贵,因为每个处理器对象都应该优化它们的资源以仅在调用
Process
时使用它们。 - 即使实例化很昂贵,使用我的原始模式,只要处理列表中有合适的对象,就会创建一个新实例。 (这可以由处理器处理,但仍然看起来一点也不好。)
- 无法根据优先级添加自定义处理器。假设
ProcessorA
处理ClassA
,ClassB
扩展ClassA
和ProcessorB
处理ClassB
。我无法阻止ProcessorA
处理ClassB
并允许其他(非自定义处理的)ClassA
后代仍然与ProcessorA
同时处理。这是因为Dictionary
结构。
所以我决定简化实现,把一个IProcessor
的可枚举直接传给主处理器,在IProcessor
中有一个CanProcess(object obj)
。这样我就可以直接使用任何 DI 容器来注入所有绑定实现的列表。