IOC Container Handling State Params in Non-Default Constructor

Posted by Mystagogue on Stack Overflow See other posts from Stack Overflow or by Mystagogue
Published on 2010-04-16T23:06:41Z Indexed on 2010/04/16 23:13 UTC
Read the original article Hit count: 578

For the purpose of this discussion, there are two kinds of parameters an object constructor might take: state dependency or service dependency. Supplying a service dependency with an IOC container is easy: DI takes over. But in contrast, state dependencies are usually only known to the client. That is, the object requestor.

It turns out that having a client supply the state params through an IOC Container is quite painful. I will show several different ways to do this, all of which have big problems, and ask the community if there is another option I'm missing. Let's begin:

Before I added an IOC container to my project code, I started with a class like this:

class Foobar {
   //parameters are state dependencies, not service dependencies
   public Foobar(string alpha, int omega){...};
   //...other stuff
}

I decide to add a Logger service depdendency to the Foobar class, which perhaps I'll provide through DI:

class Foobar {
    public Foobar(string alpha, int omega, ILogger log){...};
    //...other stuff
}

But then I'm also told I need to make class Foobar itself "swappable." That is, I'm required to service-locate a Foobar instance. I add a new interface into the mix:

class Foobar : IFoobar {
    public Foobar(string alpha, int omega, ILogger log){...};
    //...other stuff
}

When I make the service locator call, it will DI the ILogger service dependency for me. Unfortunately the same is not true of the state dependencies Alpha and Omega. Some containers offer a syntax to address this:

//Unity 2.0 pseudo-ish code:
myContainer.Resolve<IFoobar>(
   new parameterOverride[] { {"alpha", "one"}, {"omega",2} } 
);

I like the feature, but I don't like that it is untyped and not evident to the developer what parameters must be passed (via intellisense, etc). So I look at another solution:

//This is a "boiler plate" heavy approach!
class Foobar : IFoobar {
   public Foobar (string alpha, int omega){...};
   //...stuff
}
class FoobarFactory : IFoobarFactory {
   public IFoobar IFoobarFactory.Create(string alpha, int omega){
      return new Foobar(alpha, omega);
   }
}

//fetch it...
myContainer.Resolve<IFoobarFactory>().Create("one", 2);

The above solves the type-safety and intellisense problem, but it (1) forced class Foobar to fetch an ILogger through a service locator rather than DI and (2) it requires me to make a bunch of boiler-plate (XXXFactory, IXXXFactory) for all varieties of Foobar implementations I might use. Should I decide to go with a pure service locator approach, it may not be a problem. But I still can't stand all the boiler-plate needed to make this work.

So then I try this:

//code named "concrete creator"
class Foobar : IFoobar {
    public Foobar(string alpha, int omega, ILogger log){...};
    static IFoobar Create(string alpha, int omega){
       //unity 2.0 pseudo-ish code.  Assume a common
       //service locator, or singleton holds the container...
       return Container.Resolve<IFoobar>(
           new parameterOverride[] {{"alpha", alpha},{"omega", omega} } 
       );
    }

//Get my instance:
Foobar.Create("alpha",2);

I actually don't mind that I'm using the concrete "Foobar" class to create an IFoobar. It represents a base concept that I don't expect to change in my code. I also don't mind the lack of type-safety in the static "Create", because it is now encapsulated. My intellisense is working too! Any concrete instance made this way will ignore the supplied state params if they don't apply (a Unity 2.0 behavior). Perhaps a different concrete implementation "FooFoobar" might have a formal arg name mismatch, but I'm still pretty happy with it.

But the big problem with this approach is that it only works effectively with Unity 2.0 (a mismatched parameter in Structure Map will throw an exception). So it is good only if I stay with Unity. The problem is, I'm beginning to like Structure Map a lot more. So now I go onto yet another option:

class Foobar : IFoobar, IFoobarInit {
   public Foobar(ILogger log){...};

   public IFoobar IFoobarInit.Initialize(string alpha, int omega){
      this.alpha = alpha; 
      this.omega = omega;
      return this;
   }
}

//now create it...     
IFoobar foo = myContainer.resolve<IFoobarInit>().Initialize("one", 2)

Now with this I've got a somewhat nice compromise with the other approaches: (1) My arguments are type-safe / intellisense aware (2) I have a choice of fetching the ILogger via DI (shown above) or service locator, (3) there is no need to make one or more seperate concrete FoobarFactory classes (contrast with the verbose "boiler-plate" example code earlier), and (4) it reasonably upholds the principle "make interfaces easy to use correctly, and hard to use incorrectly." At least it arguably is no worse than the alternatives previously discussed.

One acceptance barrier yet remains: I also want to apply "design by contract."

Every sample I presented was intentionally favoring constructor injection (for state dependencies) because I want to preserve "invariant" support as most commonly practiced. Namely, the invariant is established when the constructor completes.

In the sample above, the invarient is not established when object construction completes. As long as I'm doing home-grown "design by contract" I could just tell developers not to test the invariant until the Initialize(...) method is called.

But more to the point, when .net 4.0 comes out I want to use its "code contract" support for design by contract. From what I read, it will not be compatible with this last approach.

Curses!

Of course it also occurs to me that my entire philosophy is off. Perhaps I'd be told that conjuring a Foobar : IFoobar via a service locator implies that it is a service - and services only have other service dependencies, they don't have state dependencies (such as the Alpha and Omega of these examples). I'm open to listening to such philosophical matters as well, but I'd also like to know what semi-authorative reference to read that would steer me down that thought path.

So now I turn it to the community. What approach should I consider that I havn't yet? Must I really believe I've exhausted my options?

© Stack Overflow or respective owner

Related posts about ioc-container

Related posts about constructor