7

I'm trying to decide whether or not it makes sense to go through the extra effort to encapsulate my IoC container. Experience tells me that I should put a layer of encapsulation between my apps and any third-party component. I just don't know if this is bordering on overkill.

I can think of situations where I might want to switch containers. For instance, my current container ceases to be maintained, or a different container is proven to be more light-weight/performant and better fits my needs. If this happens, then I'll potentially have a lot of re-wiring to do.

To be clear, I'm considering encapsulation of the registration and resolution of types. I think it's a no-brainer to encapsulate resolution - I'd hope it's common practice to have a helper/util class delegating to the container.

EDIT:

The assumption is that I prefer to wire-up my types programmatically for type-safety, compile-time checking and refactorability. It's this code and its dependency on the container that I'm looking to protect myself from.

I've also been using an IoC container for several other projects that share a lot of the same relationships, but the container is a pain to work with so I want change. But, a change means I lose the reusability of the registration code. Hence, why I'm contemplating encapsulation. It's not a huge burden, but one that I'd, nevertheless, like to mitigate.

I'm looking to:

  • Minimize the impact of change in containers / versions of containers
  • Provide some level of type-registration consistency across projects that may use different containers
  • Provide interface methods that make sense to me (RegisterSingleton<T,T> rather than RegisterType<T,T>( SomeLifetimeProvider ) - using Unity as an example).
  • Augment the container as conditions/scalability requirements change e.g. adding better caching, logging, etc during resolution/registration.
  • Provide my own model for registering type mappings.
    • Say I want to create a bunch of RegistrationHandler objects in an assembly/package and so I can easily segregate registration responsibilities across multiple classes and automatically pickup these handlers without changing code anywhere else.

I realize this is a bit subjective, so pros/cons might be helpful

Thanks!

Ryan Emerle
  • 15,461
  • 8
  • 52
  • 69
  • 1
    It's Been Done. (For .NET, at least.) [Common Service Locator library](http://www.codeplex.com/CommonServiceLocator) In any case I agree with Mr. Knesek. You generally only need dependencies on your container in your top-level class. – TrueWill Nov 19 '09 at 02:31
  • That seems a bit like overkill for what I'm trying to achieve. I just want to minimize the impact of change should I choose to change containers (in my current project, or future projects). I don't want to introduce _another_ third-party library to accomplish this. – Ryan Emerle Nov 19 '09 at 13:53
  • Addressed substaintially uin http://davybrion.com/blog/2009/11/integrating-your-ioc-container-with-agatha/ – Ruben Bartelink Nov 15 '12 at 14:57

4 Answers4

7

Do it later, and only if you actually have the need to change IOC containers.

Pick an IOC container that is non-invasive. That is, one where the objects being connected to each other don't have any dependencies on the IOC container. In this case, there's nothing to encapsulate.

If you have to pick an IOC container that requires that you have dependencies on the container, choose one with the simplest dependencies/API you can. If you need to replace this IOC container (and you probably won't), implement adapters that bridge the new API to the old one.

In other words, let the first IOC container be the one that defines the interfaces for any future container so that you don't have to invent your own, and you can delay any of this sort of work until you absolutely need it.

EDIT:

I don't see a way of guaranteeing type-safety short of either:

  1. Designing a relatively complex implementation of the Builder pattern along with visitor implementations that would write IOC configuration files, or something equivalent.
  2. Implementing a type-safe IOC configuration DSL. (My choice if I had multiple apps that required swappable IOC containers.)
Doug Knesek
  • 6,527
  • 3
  • 21
  • 26
  • The type resolution is typically non-invasive, by nature; it's the registration that typically requires a lot of container-specific code. It's this code (assuming I prefer type-safety over XML configuration) that I'm trying to protect myself (and my app(s) from) I agree that steering clear of declarative type mapping is dangerous (such as through annotations or attributes). – Ryan Emerle Nov 19 '09 at 13:56
  • Additionally, I'm not totally satisfied with _any_ of the current IoC containers' interfaces, so I don't know that I want the one I choose to dictate. – Ryan Emerle Nov 19 '09 at 14:19
  • 1
    @Ryan: While I agree with type-safety over XML, I think you'll find that you have one class (possibly even one method) that performs all the registration for a given project. Depending on the number of projects you have, abstracting registration may provide little benefit. – TrueWill Nov 19 '09 at 17:27
1

Yeah go for it. It's not a whole lot of extra effort and like you say, it gives you better isolation from third party components.

It also means that you can easily switch out the IoC container if you find something that's better. I recently did this with swapping out the Spring.net IoC container for structuremap.

The ASP.NET MVC Contrib project on codeplex is a pretty good place to start. This is what I based my implementation off.

lomaxx
  • 113,627
  • 57
  • 144
  • 179
1

It's best practice to do something only if there's an actual need for it, and never code something that you guess to be required sometimes in the future (that's the so-called YAGNI-principle). If your architecture is ok, you can easily change the container, if it actually should become necessary...

If you think you need this kind of flexibility, you may look at the Common Service Locator project at CodePlex. It does exactly what you look for: providing a common facade for various IoC containers.

HTH!

Thomas Weller
  • 11,631
  • 3
  • 26
  • 34
  • Sometimes you need to make an educated guess on the maintenance needs of your application in the future. I prefer to have some level of future-proofing, though I am whole-heartedly guided by the YAGNI principle. A single, reusable, encapsulation library (and associated tests) require such minimal effort that it's worth considering now. – Ryan Emerle Nov 19 '09 at 13:50
  • 1
    I'm with Thomas (and Doug) - it only *seems* like minimal effort now. It would add more code to test, more code that can break, and more code to maintain. And for what? To save you a little time on something that might never happen. I'd even say *probably* won't ever happen, because the kinds of things you'd change containers for (for example, to go from attribute-driven injection to declarative mappings) are the kinds of things that wouldn't be covered by an encapsulating interface. – Jeff Sternal Nov 19 '09 at 14:30
  • 1
    Just out of curiosity: What kind of application would have "changing the IoC container implementation" as a maintenace need? And btw: If it requires "minimal effort" to encapsulate the container now, then it will require even less effort to change it in the future, should this need really ever arise... – Thomas Weller Nov 19 '09 at 17:20
1

Rather than encapsulating the IOC container itself, I prefer to isolate the locus of interaction with the IOC container. For example, in ASP.Net MVC, I generally limit the exposure to the container to the controller factory and the global.aspx.cs file, where it's usually setup.

In my mind, having a lot of code that knows about the IOC container is an antipattern that increases complexity. I've seen a fair amount of code in which objects feel free to ask the IOC container for their dependencies, and then they've basically reduced the IOC container to a high-maintenance Service Locator.

Since IOC containers can resolve dependencies to an arbitrary degree of depth, it's pretty easy to make the controller factory the component that's responsible for involving the inversion of control containers. The constructor for each controller essentially specifies the services/repositories/gateways it needs.

For any of my apps, swapping the IOC container would essentially be a matter of rewriting the code the configures the container (specifies the bindings, etc.) and hooks up the controller factory. For apps exposed as services, the same basic idea should be reasonably manageable, though depending on the constraints of your runtime, you might have to use setter injection rather than constructor injection.

JasonTrue
  • 19,244
  • 4
  • 34
  • 61