Dependency injection 是否有用于初始化通过DI容器创建的对象的模式
我正试图让Unity管理对象的创建,我希望有一些直到运行时才知道的初始化参数: 目前我唯一能想到的方法就是在接口上有一个Init方法Dependency injection 是否有用于初始化通过DI容器创建的对象的模式,dependency-injection,inversion-of-control,unity-container,ioc-container,interface-design,Dependency Injection,Inversion Of Control,Unity Container,Ioc Container,Interface Design,我正试图让Unity管理对象的创建,我希望有一些直到运行时才知道的初始化参数: 目前我唯一能想到的方法就是在接口上有一个Init方法 接口IMyIntf{ void初始化(字符串runTimeParam); 字符串RunTimeParam{get;} } 然后使用它(在统一中),我会这样做: var IMyIntf=unityContainer.Resolve(); IMyIntf.Initialize(“somevalue”); 在此场景中,runTimeParamparam在运行时根据用
接口IMyIntf{
void初始化(字符串runTimeParam);
字符串RunTimeParam{get;}
}
然后使用它(在统一中),我会这样做:
var IMyIntf=unityContainer.Resolve();
IMyIntf.Initialize(“somevalue”);
在此场景中,runTimeParam
param在运行时根据用户输入确定。这里的小例子只是返回runTimeParam
的值,但实际上该参数类似于文件名,initialize方法将对文件进行处理
这会产生许多问题,即初始化
方法在接口上可用,并且可以多次调用。在实现中设置一个标志,并在重复调用Initialize
时抛出异常,这看起来很笨拙
在解析接口时,我不想知道任何关于IMyIntf
实现的信息。不过,我真正想要的是知道这个接口需要某些一次性初始化参数。有没有一种方法可以在创建对象时以某种方式用这些信息注释(属性?)接口并将其传递给框架
编辑:更详细地描述了界面。我无法用具体的Unity术语回答,但听起来您只是在学习依赖注入。如果是这样的话,我强烈要求你阅读简短、清晰、包装齐全的信息 这将引导您了解在使用DI时的各种选择,以及如何说明在使用过程中将面临的具体问题。在您的情况下,您很可能希望使用DI容器来实例化您的对象,并让该对象通过构造函数获得对其每个依赖项的引用 本演练还详细介绍了如何在运行时使用属性对方法、属性甚至参数进行注释以区分它们
即使您不使用Ninject,本演练也会为您提供适合您的目的的功能的概念和术语,您应该能够将这些知识映射到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;
类MyIntfImpl:IMyIntf,IMyIntfSetter{
字符串_runTimeParam;
无效初始化(字符串runTimeParam){
_runTimeParam=runTimeParam;
}
字符串RunTimeParam{get;}
IMyIntf MyIntf{get{返回此;}
}
//统一配置:
//只有setter映射到实现。
container.RegisterType();
//要检索IMyIntf的实例,请执行以下操作:
//1. 创建setter
imyintsetter setter=container.Resolve();
//2. 初始化它
setter.Initialize(“someparam”);
//3. 使用IMyIntf访问器
IMyIntf intf=setter.MyIntf;
imyintsetter.Initialize()
仍然可以被多次调用,但使用一些函数,我们可以很好地将其封装起来,这样imyintsetter
几乎就是一个内部接口,通常在遇到这种情况时,它与IMyIntf
不同,您需要重新审视您的设计,并确定是否将有状态/数据对象与纯服务混合在一起。在大多数(并非所有)情况下,您将希望将这两种类型的对象保持分离
如果确实需要在构造函数中传递特定于上下文的参数,一个选项是创建一个工厂,该工厂通过构造函数解析服务依赖项,并将运行时参数作为create()方法(或Generate()、Build()或任何您命名的工厂方法)的参数
拥有setter或Initialize()方法通常被认为是糟糕的设计,因为您需要“记住”调用它们,并确保它们不会打开太多的实现状态(即,如何阻止某人重新调用Initialize或setter?).任何需要运行时值来构造特定依赖项的地方,抽象工厂是解决方案 在接口上使用初始化方法有一种泄漏抽象的味道 在您的例子中,我想说的是,您应该根据您需要如何使用它来建模
IMyIntf
接口,而不是您打算如何创建它的实现。这是一个实现细节
因此,接口应该是:
公共接口IMyIntf
{
字符串RunTimeParam{get;}
}
现在定义抽象工厂:
公共接口imyintfactory
{
IMyIntf Create(字符串runTimeParam);
}
您现在可以创建imyintfactory
的具体实现,该实现创建了IMyIntf
的具体实例,如下所示:
公共类MyIntf:IMyIntf
{
私有只读字符串runTimeParam;
公共MyIntf(字符串运行时参数)
{
if(runTimeParam==null)
{
抛出新ArgumentNullException(“runTimeParam”);
}
this.runTimeParam=runTimeParam;
}
公共字符串运行时参数
{
获取{返回this.runTimeParam;}
}
}
请注意,这允许我们通过使用readonly
关键字保护类的不变量。没有臭的初始化方法是必要的
imyintfactory
实现可能非常简单:
公共类MyIntfFactory:IMyIntfFactory
{
公共IMyIntf Create(字符串runTimeParam)
{
返回新的MyIntf(runTimeParam);
}
}
在你所有的时间里