IoC/DI容器、工厂和运行时类型创建
本文关键字:运行时 类型 创建 工厂 DI 容器 IoC | 更新日期: 2023-09-27 18:27:08
我最近了解了DI框架Guice和Ninject,并希望在我的一些新项目中使用它们。
虽然我熟悉一般的依赖注入概念,知道如何使用这些框架来构建对象图,但当涉及到动态应用程序行为时,我很难应用IoC。
考虑这个例子:
- 当应用程序启动时,将显示主窗口
- 当用户单击主面板时,会打开一个上下文菜单
- 根据用户的选择,将创建新的用户控件并显示在鼠标位置
- 如果用户最终决定关闭应用程序,将显示一个确认框,确认后,主窗口将关闭
虽然很容易将主窗口的视图连接到Presenter/ViewModel,然后将其绑定到域逻辑,但我不明白如何干净地(在IoC的意义上)实现以下任务:
-
动态实例化具体的UI控件(例如
IGreenBoxView
、IRedImageView
<-JConcreteGreenBoxView
、JConcreteRedImageView
),而不使用任何类型的服务定位器模式(例如再次从IoC请求)- 根据情况,创建一个新的模型、演示者和视图实例
- 同样,在运行时设置一个新的具体对话框,例如
JOptionPane
我见过一些使用抽象工厂的解决方案,但老实说,我并不完全理解它们。这样的解决方案似乎会将一些(视图域、演示者域…)内部类型暴露给构造根,并由此暴露给整个世界。
那么,我该怎么做呢?
如果您可以重用控件,那么您可以在使用它们的地方进行构造函数注入。否则你必须注入一个工厂:
public interface IControlFactory
{
IGreenBoxView CreateGreenBoxView();
IRedImageView CreateRedImageView();
}
并将其注入到需要创建此控件的位置。
实现转到容器配置。在那里,您可以将容器注入到实现中。一些容器提供自动实现此工厂。例如,在Ninject:中
Bind<IControlFactory>().ToFactory();
请参阅https://github.com/ninject/ninject.extensions.factory/wiki
对于那些希望用Unity(而不是Ninject)做类似事情的人,我创建了一个扩展,允许您创建工厂,甚至不必声明接口:UnityMappingFactory@GitHub
在正常的引导过程中,您只需在注册类的地方添加映射。。。
//make sure to register the output...
container.RegisterType<IImageWidgetViewModel, ImageWidgetViewModel>();
container.RegisterType<ITextWidgetViewModel, TextWidgetViewModel>();
//define the mapping between different class hierarchies...
container.RegisterFactory<IWidget, IWidgetViewModel>()
.AddMap<IImageWidget, IImageWidgetViewModel>()
.AddMap<ITextWidget, ITextWidgetViewModel>();
然后,您只需在CI的构造函数中声明映射工厂接口,并使用其Create()方法。。。
public ImageWidgetViewModel(IImageWidget widget, IAnotherDependency d) { }
public TextWidgetViewModel(ITextWidget widget) { }
public ContainerViewModel(object data, IFactory<IWidget, IWidgetViewModel> factory)
{
IList<IWidgetViewModel> children = new List<IWidgetViewModel>();
foreach (IWidget w in data.Widgets)
children.Add(factory.Create(w));
}
额外的好处是,映射类的构造函数中的任何其他依赖项也将在对象创建过程中得到解决。(也作为对此Stackoverflow帖子的回答分享)