Is there a pattern for initializing objects created via a DI container
I am trying to get Unity to manage the creation of my objects and I want to have some initialization parameters that are not known until run-time:
At the moment the only way I could think of the way to do it is to have an Init method on the interface.
interface IMyIntf {
void Initialize(string runTimeParam);
string RunTimeParam { get; }
}
Then to use it (in Unity) I would do this:
var IMyIntf = unityContainer.Resolve<IMyIntf>();
IMyIntf.Initialize("somevalue");
In this scenario runTimeParam
param is determined at run-time based on user input. The trivial case here simply returns the value of runTimeParam
but in reality the parameter will be something like file name and initialize method will do something with the file.
This creates a number of issues, namely that the Initialize
method is available on the interface and can be called multiple times. Setting a flag in the implementation and throwing exception on repeated call to Initialize
seems way clunky.
At the point where I resolve my interface I don't want to know anything about the implementation of IMyIntf
. What I do want, though, is the knowledge that this interface needs certain one time initialization parameters. Is there a way to somehow annotate(attributes?) the interface with this information and pass those to framework when the object is created?
Edit: Described the interface a bit more.
Any place where you need a run-time value to construct a particular dependency, Abstract Factory is the solution.
Having Initialize methods on the interfaces smells of a Leaky Abstraction .
In your case I would say that you should model the IMyIntf
interface on how you need to use it - not how you intent to create implementations thereof. That's an implementation detail.
Thus, the interface should simply be:
public interface IMyIntf
{
string RunTimeParam { get; }
}
Now define the Abstract Factory:
public interface IMyIntfFactory
{
IMyIntf Create(string runTimeParam);
}
You can now create a concrete implementation of IMyIntfFactory
that creates concrete instances of IMyIntf
like this one:
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; }
}
}
Notice how this allows us to protect the class' invariants by use of the readonly
keyword. No smelly Initialize methods are necessary.
An IMyIntfFactory
implementation may be as simple as this:
public class MyIntfFactory : IMyIntfFactory
{
public IMyIntf Create(string runTimeParam)
{
return new MyIntf(runTimeParam);
}
}
In all your consumers where you need an IMyIntf
instance, you simply take a dependency on IMyIntfFactory
by requesting it through Constructor Injection .
Any DI Container worth its salt will be able to auto-wire an IMyIntfFactory
instance for you if you register it correctly.
Usually when you encounter this situation, you need to revisit your design and determine if you are mixing your stateful/data objects with your pure services. In most (not all) cases, you will want to keep these two types of objects separate.
If you do need a context-specific parameter passed in the constructor, one option is to create a factory that resolves your service dependencies via the constructor, and takes your run-time parameter as a parameter of the Create() method (or Generate(), Build() or whatever you name your factory methods).
Having setters or an Initialize() method are generally thought to be bad design, as you need to "remember" to call them and make sure they don't open up too much of your implementation's state (ie what is to stop someone from re-calling initialize or the setter?).
I also have come across this situation a few times in environments where I am dynamically creating ViewModel objects based on Model objects (outlined really well by this other Stackoverflow post).
I liked how the Ninject extension which allows you to dynamically create factories based on interfaces:
Bind<IMyFactory>().ToFactory();
I could not find any similar functionality directly in Unity ; so I wrote my own extension to the IUnityContainer which allows you to register factories that will create new objects based on data from existing objects essentially mapping from one type hierarchy to a different type hierarchy: UnityMappingFactory@GitHub
With a goal of simplicity and readability, I ended up with an extension that allows you to directly specify the mappings without declaring individual factory classes or interfaces (a real time saver). You just add the mappings right where you register the classes during the normal bootstrapping process...
//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>();
Then you just declare the mapping factory interface in the constructor for CI and use its Create() method...
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));
}
As an added bonus, any additional dependencies in the constructor of the mapped classes will also get resolved during object creation.
Obviously, this won't solve every problem but it has served me pretty well so far so I thought I should share it. There is more documentation on the project's site on GitHub.
链接地址: http://www.djcxy.com/p/77774.html上一篇: 我应该在哪里使用Ninject 2+进行注射(以及如何安排模块?)
下一篇: 是否有初始化通过DI容器创建的对象的模式