2014-08-21

IoC - good, bad and ugly

I was reading this article that is doing a pretty good job at explaining in what situations an IoC container saves the day. And even though I dislike most uses of IoC containers I've seen I have found them useful once or twice.

Just like the article states above IoC containers are perfect for a certain type of architecture. One of them being that you have abstractions that have different implementation. In real applications however I've rarely seen this as a common thing. Most of the time whoever needs an object created actually knows exactly what object to create.

Good IoC container usage is not much different than using constructor injection because the IoC container is not used to create dependencies needed by the object but rather used to create new objects. This usage is good because each object is still declaring its dependencies through (typically) constructor injection while allowing for flexibility when it comes to creating new objects. And since the object resolves dependencies for any child object created it is easy to figure out the partial dependency graph needed in for example a unit test. Here is an example to illustrate what good IoC usage would look like:

Bad IoC container usage is when the IoC container is injected through the constructor to an object so that the object can resolve whatever dependencies it has when created. That means all dependencies are resolved in the constructor. The main reason this is bad is because it is now hard to figure out the dependency graph which makes your code hard to understand.

Ugly IoC container usage is anything that either uses a singleton to access the IoC container or let's the IoC container automatically resolve dependencies in constructors.  Your IoC container usage is also ugly if you resolve your dependencies anywhere other than in the constructor of the object.

Preemptive comments:
  • While I was talking about IoC containers above the same reasoning applies even if using abstract (or concreate) factories rather than service locators.
  • Integrating with certain frameworks where the framework is creating objects for you means you might need to use an IoC container but then you can use it for that purpose only and not for all objects in your code.
  • Yes if your objects has a lot of dependencies making your constructor huge you either have a design problem or you are working on one of those coordinator type objects that needs to coordinate a lot of things. In that case it might be fine to use a factory instead of an IoC container. At least the factory would explicitly declare your dependencies.
  • If you find that you really want to use an IoC container please look at autofac - it will more or less force you to stay away from the ugly patterns at lease.
  • Yes in my good example above I use the new keyword which means MyObject has a dependency on SomeChildObject. Most of the time that is actually OK and only occasionally do I see the need to hide how the dependant object is created from the parent object. When that is the case the parent object just needs to get a child object factory as one of its arguments rather than using the IoC container to resolve the interface. Here is an example of what that would look like.

8 comments:

  1. You need a really large caveat to your bad section. This is bad when you use it in place of dependency injection. There is a radically different scenario if you actually need service location. With enough kungfu with containers you can move service location into the container itself and have it inject the service you need but that will lead to extremely difficult to test logic that is completely buried in goo of your infrastructure.

    An example of where service location is valued over dependency injection

    EncodedMessage Processor(Message msg)
    {
    var encoder = Locate(msg.ContentType)
    return encoder.Encode(msg)
    }

    In this scenario you cannot reasonably use dependency injection. Because Process can handle numerous messages of different ContentTypes you cannot reasonably bind a single Encoder in through dependency injection as it is not a static dependency.

    Of course you could inject List encoders and do encoders.Where(x=> x.Handles(msg.ContentType).Single().Encode(msg) but you just implemented service location anyway. The downside to the naive implementation such as that Where statement above is you lose all the features of the container to manage life cycle support. Maybe you need an unique instance for every encoder usage. Even if it is still vanilla service location you're duplicating functionality provided by the container for no net gain.

    I would agree with you that is Bad if you are not branching off runtime data such as a Type property. If there is no branching occurring that should be dependency injection.

    ReplyDelete
    Replies
    1. I think there is a huge difference between a service locator, an abstract factory or a custom factory (interesting read on that here: http://blog.ploeh.dk/2010/11/01/PatternRecognitionAbstractFactoryorServiceLocator/).

      I can easily turn your example from bad to good by saying that the service locator is an instance in my class. That is, my class will need a "encoder resolver" instance. I agree a list of encoders is not the best way to do it. The good thing with injecting the encoder resolver separately is that I now declare my dependencies; I need an encoder resolver. Then the encoder resolver itself can have a list of encoders injected or whatever is best there.

      They way I read your example code is actually that you are describing the same thing I did the in good example...

      Delete
  2. Actually reading your "good usage" of a container section closer. That's a text book example of **bad usage**. this.container.Resolve() is service location for no purpose. The correct way to do your example is using modern IOC containers that support factory injection.

    class MyObject
    {
    private ISomeDependency dependency;
    private Lazy myLowLevelDependencyFactory;
    public MyObject(ISomeDependency dependency, Lazy myLowLevelDependencyFactory)
    {
    this.dependency = dependency;
    this.myLowLevelDependencyFactory= myLowLevelDependencyFactory;
    }
    public IChildObject CreateChildObject()
    {
    return new SomeChildObject(myLowLevelDependencyFactory.Value);
    }
    }

    I hope my 2 posts have you rethink your understanding of dependency injection and service location as you have made the most common anti-pattern mistakes.

    ReplyDelete
    Replies
    1. So I actually agree with you 100%. I hate IoC containers... I think it is much better to have explicit factories... And to inject them explicitly rather than relying on IoC magic to do it. This post was to show the only decent use of IoC containers I see. IMHO it is always better to use other patterns than a service locator. But if you really insist on using them I tried to illustrate how to use them in the least bad way...

      Delete
    2. If you need to use a service locator, you need to actually be locating something that varies during runtime.

      this.container.Resolve<IMyLowLevelDependency>()

      will never vary at runtime unless you're doing very weird things that you're actually modifying the container while the site is running.

      You mention Seemann's blog http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/ and the example usage you have is ultimately the same. I've disagreed with Mark for years and you can find my comments with ctrl+F over there.

      In the conclusion of his article he makes the statement "It will give consumers of your API a horrible developer experience" that is just false. Proper usage of service location will give users of your API a GREAT experience. They will be unconcerned with the variation that goes under the hood because it is not relevant to them.

      Here's an example baked in ASP.NET WebApi

      config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html") );

      In there you're configuring the JsonFormatter to respond to the meda type "text/html" this goes right along with my example above. Instead of having to inject the JsonFormatter into everything, service location will use the MediaType to pick the appropriate formatter. I really don't understand how Mark could be so wrong when it comes to a design pattern. Without service location this API would be a nightmare to work.

      Service location solves the specific problem: given data available only at run time and is varied i need to locate a matching service.

      There is no other pattern on the planet that does this except poor implementations of the service location pattern. To use service location without the varying part is indeed an anti-pattern.

      Delete
    3. Somewhere I think you have made an equal sign between IoC and service location (the same way I made an equal sign between IoC and dependency injecton). Locating or creating an object based on data only available at runtime has nothing to do with IoC containers. Or at least not the way most people use IoC containers. Because they are more used as dependency injection tools rather than service locators in the form you describe. Using the service locator pattern by implementing a factory (ex: "FormatterFactory") is a good usage of the pattern in my opinion and should in many cases be encouraged.

      The terrible user experience I refer to has to do with IoC containers used for dependency injection (which is the most use I see of it). Using an IoC container for service location in the form you describe is fine I think. But I prefer an explicit service locator over a generic one...

      Delete
  3. I think Google ate my comment. I've posted it in its entirety here: https://gist.github.com/nelsonlaquet/7d3c8088ec39d5ee242b

    ReplyDelete
    Replies
    1. An excellent description of what I tried to say (kind of... I think...).
      First of all I agree that your comment on taking factories rather than dependencies and how factories most of the time should just be 'Func' instances. And as you point out good use of IoC containers typically means you don't see them nor rely on them in your code.
      What I tried to point out was how to use them when you actually can't avoid it. One reason being all the cumbersome constructors and component factories you have in your examples.
      A static/singleton resolver used all over the place is then IMHO the "ugly" variant. Since it makes testing hard since all tests now share the same IoC container.
      If we instead the IoC container but keep it around and use it all over the place in our code under test; that is the bad part according to me.
      Which leaves us with the "good" option; take an instance of the resolver and resolve dependencies in the constructor only. What is happening in my example is that I'm not resolving a particular dependency in the constructor but rather when the object is created. I agree that taking a factory (as you suggest) is IMO far better (or even resolving a factory in the constructor).
      What I was trying to illustrate is that the IoC container should be used to resolve dependencies for objects created, not to resolve your own dependencies. But again; creating objects through factories as you described is also my preferred way of doing things.

      Delete