views:

1004

answers:

4

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.

A: 

I can't answer with specific Unity terminology but it sounds like you are just learning about dependency injection. If so, I urge you to read the brief, clear, and information packed user guide for Ninject.

This will walk you through the various option you have when using DI, and how to account for the specific issues that you'll face along the way. In your case, you would most likely want to use the DI container to instantiate your objects, and have that object get a reference to each of its dependencies through the constructor.

The walkthrough also details how to annotate methods, properties, and even parameters using attributes to distinguish them at runtime.

Even if you do not use Ninject, the walkthrough will give you the concepts and terminology of the functionality that suits your purpose, and you should be able to map that knowledge to Unity or other DI frameworks (or convince yout to give Ninject a try).

anthony
THanks for that. I am actually evaluating DI frameworks and NInject was going to be my next one.
Igor Zevaka
A: 

I think I solved it and it feels rather wholesome, so it must be half right :))

I split IMyIntf into a "getter" and a "setter" interfaces. So:

interface IMyIntf {
  string RunTimeParam { get; }
}


interface IMyIntfSetter {
  void Initialize(string runTimeParam);
  IMyIntf MyIntf {get; }
}

Then the implementation:

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() can still be called multiple times but using bits of Service Locator paradigm we can wrap it up quite nicely so that IMyIntfSetter is almost an internal interface that is distinct from IMyIntf.

Igor Zevaka
This isn't a particularly good solution since it relies on an Initialize method, which is a Leaky Abstraction. Btw, this doesn't look like Service Locator, but more like Interface Injection. In any case, see my answer for a better solution.
Mark Seemann
+1  A: 

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?).

Phil Sandler
+16  A: 

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 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.

Mark Seemann
This is great, I think it solves most of my concerns, except for the Factory interface having a dependency on `MyIntf` class. Am I just trying to get my DI container to do too much? I guess there is only one place where there is a dependency on the interface implementation, so if I want to swap it out for another implementation I just write another factory. Don't worry about answering that, I already answered it for myself :))
Igor Zevaka
this, imho, is an excellent solution to this problem.
qstarin