是否有初始化通过DI容器创建的对象的模式
我试图让Unity来管理我的对象的创建,并且我希望有一些初始化参数在运行时间之前是未知的:
目前我唯一能想到的方法是在界面上使用Init方法。
interface IMyIntf {
void Initialize(string runTimeParam);
string RunTimeParam { get; }
}
然后使用它(在Unity中)我会这样做:
var IMyIntf = unityContainer.Resolve<IMyIntf>();
IMyIntf.Initialize("somevalue");
在这种情况下, runTimeParam
参数是根据用户输入在运行时确定的。 这里的简单情况只是返回runTimeParam
的值,但实际上参数是类似文件名的,而initialize方法会对文件做些什么。
这会产生很多问题,即Initialize
方法在接口上可用并且可以多次调用。 在实现中设置一个标志并在重复调用Initialize
抛出异常看起来很笨重。
在我解决界面问题的时候,我不想了解IMyIntf
的实现。 但是,我所需要的是知道此接口需要某个一次性初始化参数。 有没有办法用这个信息注释(属性?)接口,并在创建对象时将它们传递给框架?
编辑:描述更多的接口。
在任何需要运行时值来构造特定依赖项的地方, Abstract Factory都是解决方案。
在一个泄漏抽象的接口上初始化方法。
在你的情况下,我会说,你应该如何建模IMyIntf
接口你需要如何使用它 - 而不是你如何创建它的实现。 这是一个实现细节。
因此,界面应该简单地是:
public interface IMyIntf
{
string RunTimeParam { get; }
}
现在定义Abstract Factory:
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
的依赖IMyIntfFactory
。
如果你注册了正确的话,任何值得它的salt的DI容器都能够为你自动连接一个IMyIntfFactory
实例。
通常当遇到这种情况时,您需要重新访问您的设计并确定您是否将有状态/数据对象与纯服务混合在一起。 在大多数情况下(不是全部),您都希望将这两种类型的对象分开。
如果您确实需要在构造函数中传递的特定于上下文的参数,则一种选择是创建一个可通过构造函数解析服务依赖项的工厂,并将您的运行时参数作为Create()方法的参数(或Generate ),Build()或任何你的工厂方法的名称)。
有setter或Initialize()方法通常被认为是糟糕的设计,因为你需要“记住”调用它们,并确保它们不会打开太多的实现状态(例如,阻止某人重新启动 - 初始化或setter?)。
在基于Model对象动态创建ViewModel对象的环境中,我也遇到过几次这种情况(其他的Stackoverflow很好地概括了这一点)。
我喜欢Ninject扩展,它允许您根据接口动态创建工厂:
Bind<IMyFactory>().ToFactory();
我在Unity中找不到任何类似的功能; 所以我为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的构造函数中声明映射工厂接口并使用其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));
}
作为额外的好处,映射类的构造函数中的任何附加依赖项也将在对象创建期间得到解决。
很明显,这并不能解决所有问题,但迄今为止我的表现相当不错,所以我认为我应该分享它。 GitHub上的项目网站上有更多的文档。
链接地址: http://www.djcxy.com/p/77773.html上一篇: Is there a pattern for initializing objects created via a DI container
下一篇: Refactor factory without violating Open Close Principle