是否有初始化通过DI容器创build的对象的模式
我想让Unity来pipe理我的对象的创build,我想有一些初始化参数,直到运行时才知道:
目前唯一能想到的方法是在界面上使用Init方法。
interface IMyIntf { void Initialize(string runTimeParam); string RunTimeParam { get; } }
然后使用它(在统一)我会这样做:
var IMyIntf = unityContainer.Resolve<IMyIntf>(); IMyIntf.Initialize("somevalue");
在这种情况下, runTimeParam
参数是在运行时根据用户input确定的。 这里的小事情只是返回runTimeParam
的值,但实际上这个参数是类似文件名的,初始化方法会对文件做一些事情。
这就产生了一些问题,即Initialize
方法在接口上可用,可以多次调用。 在实现中设置一个标志,并抛出exception重复调用Initialize
似乎是笨重的。
在我解决我的界面的时候,我不想了解IMyIntf
的实现。 我所需要的是,这个接口需要一定的初始化参数。 有没有办法用这个信息注释(属性?)接口,并在创build对象时将它们传递给框架?
编辑:描述更多的接口。
在任何需要运行时值来构造特定依赖项的地方, 抽象工厂就是解决scheme。
初始化泄漏抽象接口的方法。
在你的情况下,我会说,你应该如何build模IMyIntf
接口如何使用它 – 而不是你如何创build它的实现。 这是一个实现细节。
因此,界面应该简单地是:
public interface IMyIntf { string RunTimeParam { get; } }
现在定义Abstract Factory:
public interface IMyIntfFactory { IMyIntf Create(string runTimeParam); }
现在可以创build一个IMyIntfFactory
的具体实现,创buildIMyIntfFactory
具体实例,如下所示:
public class MyIntf : IMyIntf { private readonly string runTimeParam; public MyIntf(string runTimeParam) { if(runTimeParam == null) { throw new ArgumentNullException("runTimeParam"); } this.runTimeParam = runTimeParam; } public string RunTimeParam { get { return this.runTimeParam; } } }
注意这是如何使我们能够通过使用readonly
关键字来保护类的不variables 。 没有臭味初始化方法是必要的。
IMyIntfFactory
实现可能如此简单:
public class MyIntfFactory : IMyIntfFactory { public IMyIntf Create(string runTimeParam) { return new MyIntf(runTimeParam); } }
在您需要IMyIntf
实例的所有消费者中,只需通过构造函数注入来请求对IMyIntfFactory
的依赖IMyIntfFactory
。
如果你正确地注册它,任何值得它的salt的DI容器都能够为你自动连接一个IMyIntfFactory
实例。
通常当遇到这种情况时,您需要重新访问您的devise,并确定是否将有状态/数据对象与纯服务混合在一起。 在大多数(不是全部)的情况下,你会想把这两种types的对象分开。
如果您确实需要在构造函数中传递的特定于上下文的参数,则一种select是创build一个工厂,通过构造函数parsing您的服务依赖关系,并将您的运行时参数作为Create()方法(或Generate ),Build()或者你的工厂方法的名称)。
有setter或Initialize()方法通常被认为是不好的devise,因为你需要“记住”调用它们,并确保它们不打开太多的实现状态(即什么是阻止某人重新启动 – 初始化或setter?)。
我也遇到过这种情况几次,我dynamic创build基于模型对象的ViewModel对象(由其他Stackoverflowpost很好地概述)。
我喜欢Ninject扩展 ,它允许你基于接口dynamic地创build工厂:
Bind<IMyFactory>().ToFactory();
我无法直接在Unity中find任何类似的function; 所以我写了我自己的扩展到IUnityContainer ,它允许你注册工厂,根据现有对象的数据创build新的对象,从一个types层次结构到另一个types层次结构: 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)); }
作为额外的好处,映射类的构造函数中的其他依赖项也将在对象创build期间得到解决。
显然,这并不能解决所有的问题,但到目前为止,我已经做得相当好了,所以我想我应该分享一下。 在GitHub上的项目网站上有更多的文档。
我无法用特定的Unity术语回答,但听起来你只是在学习dependency injection。 如果是这样,我build议您阅读Ninject的简要,清晰和信息包装的用户指南 。
这将引导您了解使用DI时的各种选项,以及如何说明您将要面对的具体问题。 在你的情况下,你很可能想使用DI容器来实例化你的对象,并让这个对象通过构造函数获得对它的每个依赖的引用。
该演练还详细介绍了如何使用属性注释方法,属性甚至参数以在运行时区分它们。
即使你不使用Ninject,演练会给你适合你的目的的function的概念和术语,你应该能够将这些知识映射到Unity或其他DI框架(或者说服你试试Ninject) 。
我觉得我解决了这个问题,感觉很健康,所以它一定是对的:))
我将IMyIntf
分为“getter”和“setter”接口。 所以:
interface IMyIntf { string RunTimeParam { get; } } interface IMyIntfSetter { void Initialize(string runTimeParam); IMyIntf MyIntf {get; } }
然后执行:
class MyIntfImpl : IMyIntf, IMyIntfSetter { string _runTimeParam; void Initialize(string runTimeParam) { _runTimeParam = runTimeParam; } string RunTimeParam { get; } IMyIntf MyIntf {get {return this;} } } //Unity configuration: //Only the setter is mapped to the implementation. container.RegisterType<IMyIntfSetter, MyIntfImpl>(); //To retrieve an instance of IMyIntf: //1. create the setter IMyIntfSetter setter = container.Resolve<IMyIntfSetter>(); //2. Init it setter.Initialize("someparam"); //3. Use the IMyIntf accessor IMyIntf intf = setter.MyIntf;
IMyIntfSetter.Initialize()
仍然可以被多次调用,但是使用Service Locator范例的位,我们可以很好地包装它,这样IMyIntfSetter
几乎是一个与IMyIntf
不同的内部接口。
- 控制反转<dependency injection
- MEF和IoC容器的区别(如Unity,Autofac,SMap,Ninject,Windsor.Spring.net等)
- 如何使用IOC从存储库中删除工作单元function
- Unity 2.0和处理IDisposabletypes(尤其是使用PerThreadLifetimeManager)
- 应用程序体系结构/在F#
- DAL – > BLL < – GUI +组合根。 如何设置DI绑定?
- 为什么我需要一个IoC容器而不是简单的DI代码?
- 通过构造函数或属性设置程序进行dependency injection?
- 如何使用MVVMLight SimpleIoc?