是否有通过DI容器中创建初始化对象的模式
题
我想获得统一来管理我的对象的创建,我想有不知道,直到运行时的一些初始化参数:
目前,我能想到的办法只有这样,才能做到这一点是有接口的初始化方法。
interface IMyIntf {
void Initialize(string runTimeParam);
string RunTimeParam { get; }
}
然后使用它(在Unity)我想这样做:
var IMyIntf = unityContainer.Resolve<IMyIntf>();
IMyIntf.Initialize("somevalue");
在这种情况下runTimeParam
PARAM在基于用户输入的运行时间被确定。这个琐碎的例子在这里简单地返回runTimeParam
的价值,但在现实中的参数将是类似的文件名和初始化方法做了一些文件。
这产生了一些问题,即该方法Initialize
是可用的界面上,并且可以被调用多次。在实施设置一个标志和重复调用抛出异常Initialize
看起来笨重的方式。
目前,我解决我的界面我不想知道IMyIntf
实施任何点。我确实想,虽然是,这个接口需要一定的一次初始化参数的知识。有一种方法以某种方式注释(属性?)和该信息的接口,并且创建对象时这些信息传递给框架?
编辑:所描述的接口更多的
解决方案
在需要运行时值来构造一个特定的依赖的任何地方,的抽象工厂强>是该溶液中。
具有在接口初始化方法的气味一个的漏抽象强>
在你的情况下,我会说,你应该在IMyIntf
接口模型的您需要如何使用它的 - 而不是你怎么意图及其创造的实现。这是一个实现细节。
因此,接口应该简单地:
public interface IMyIntf
{
string RunTimeParam { get; }
}
现在定义抽象工厂:
public interface IMyIntfFactory
{
IMyIntf Create(string runTimeParam);
}
您现在可以创建一个具体的实施IMyIntfFactory
的创建IMyIntf
的具体实例像这样的:
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
关键字。无臭初始化方法是必要的。
这是IMyIntfFactory
实现可以是像这样简单:
public class MyIntfFactory : IMyIntfFactory
{
public IMyIntf Create(string runTimeParam)
{
return new MyIntf(runTimeParam);
}
}
在所有的消费者,你需要一个IMyIntf
情况下,您只需承担IMyIntfFactory
依赖通过请求它通过构造注入
任何DI容器值得其盐将能够自动线你的一个IMyIntfFactory
例如,如果你正确地注册。
其他提示
通常当你遇到这种情况,您需要重新设计,并确定如果你与你的纯服务混合有状态/数据对象。在大多数(不是全部)的情况下,你会希望保持这两种类型的对象分开。
如果你需要在构造函数中通过一个特定的上下文参数,一个选择是创建一个工厂,通过构造解析服务的依赖,并把你的运行时间参数为Create()方法的参数(或生成(),建立()或任何你命名你的工厂方法)。
有制定者或初始化()方法是一般的认为是糟糕的设计,因为你需要“记住”打电话给他们,并确保他们不要打开太多的实现的的状态(即什么是从阻止别人再调用初始化或setter方法?)。
我也有在我动态创建视图模型对象的环境中碰到过这种情况了几次基于模型对象(本其他的后#1 )。
我喜欢如何 Ninject扩展它允许你动态地创建工厂基于接口:
Bind<IMyFactory>().ToFactory();
我不能直接在找到任何类似的功能的统一强>;所以我写我自己扩展到 IUnityContainer 它允许您注册将基于现有对象从一个类型层次基本上是映射到不同类型层次的数据创建新的对象工厂:的 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和使用其的创建()的方法...
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));
}
作为额外的奖励,在被映射的类的构造器的任何附加的依赖性也将得到对象创建过程中解决。
显然,这并不能解决所有问题,但它一直担任我很好,到目前为止所以我想我应该分享。目前该项目的GitHub上的网站更多的文档。
我无法与特定的统一术语回答,但它听起来就像你刚开始学习有关依赖性注入。如果是的话,我劝你阅读简短,清晰,信息打包用户指南为Ninject 。
这将引导您完成您在使用DI时,如何解释,你会一路上遇到的具体问题有不同的选择。在你的情况,你最有可能要使用DI容器来实例对象,有对象打通构造参考它的每个依赖。
在演练还详细介绍了如何注释方法,属性,甚至使用属性参数在运行时对它们进行区分。
即使你不使用Ninject,演练会给你的概念和适合你的目的的功能的术语,你应该能够映射这些知识统一或其他DI框架(或说服YOUT给Ninject一试)。
我想我解决了它,感觉比较健康,所以一定是说对了一半:))
我分裂IMyIntf
成“吸气剂”和“调节器”的接口。所以:
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()
仍然可以多次调用但服务定位器模式的使用位我们可以包起来很好地使得IMyIntfSetter
几乎是一个内部接口,它从IMyIntf
不同。