Is there a pattern for initializing objects created via a DI container
Solution 1:
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.
Solution 2:
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 (i.e. what is to stop someone from re-calling initialize or the setter?).