A philosophical discussion about Inversion of Control frameworks

I had a short conversation with Chad Myers today over twitter (a
very challenging medium to have a conversation of any substance over,
and frustrating). Basically, it was about my effort with Siege.ServiceLocation. I thought I would clear up some misconceptions.

 

Siege.ServiceLocation isn’t an abstraction, it’s a decoupling.

Siege.ServiceLocation isn’t just about contextual
registration/resolution (though that was the initial impetus). It’s not
about resolution at all. It’s about expressing registration. It’s about
adding functionality to ALL containers. There are a few things that I
am trying to accomplish with this library. First, I want to decouple
registration from resolution. That’s not to say I want to abstract how
each container’s registration is performed; I want them decoupled completely. Second, I want my library to be functionally compatible with existing frameworks (see Siege Design Philosophy
for more details). This means whatever Siege.ServiceLocation can do,
all the frameworks it integrates with can do. Third, I want it to be
easy. Outside of Ninject (which I think is a wonderfully built IoC), I
think the others fail the third criteria. I’ve worked with them all. I
know the differences. They each have distinct problems. And I don’t
think it has to be that way.

 

Inversion of Control containers should be about resolving types

Not about registering types. Registration is a way of instructing an
IoC how to resolve a type. It’s not what the IoC is built for, it’s a
way of expressing what the IoC needs to do. Each IoC has a different
way of accomplishing the same thing. I’ve been told that choosing an
IoC is about evaluating the strengths and weaknesses of each framework
and picking the one that suits your needs.

What? If an IoC is there to resolve types and their dependencies, to
provide a mechanism to help you decouple your code, then how can there
be strengths and weaknesses in each framework? Is it in terms of how
efficiently they perform the task? Is it in terms of they internally
manage resolution?

Or is it in terms of what their registration syntax allows you to
do? Is it in terms of how you express your goal? Is it in terms of the
instructions the container understands when it tries to resolve a type?

These are all rhetorical questions. The point is to look at the
problem differently than has been traditionally done. To my mind, there
is a clear seperation of concerns between the responsibility of
registering types and the responsibility of resolving types. I think an
IoC should specialize in resolving types and their dependencies, not
specialize in expressing how to resolve types and their dependencies.

 

Siege.ServiceLocation is not an IoC

It’s a coordination mechanism for an IoC container. Think “one level
above” the container. Rather than instructing an IoC container how to
resolve your type, you instruct Siege.ServiceLocation. It then in turn
knows how to interact with the underlying framework to get what you
want. It tracks the conditions and criteria. It makes all the requests
necessary through the underlying framework to get your objects
constructed. Think of it as a proxy with an adapter. The goal is to
track runtime conditions to facilitate the selection of an appropriate
interface. To enable this functionality WITHOUT having to force you to
use a specific framework. Where the IoC container specializes in
resolution, Siege.ServiceLocation specializes in registration and
communicating requests to the IoC container.

The reason that each IoC framework has strengths and weaknesses is
because each IoC has it’s own way of trying to go about describing how
they want things resolved. That means that some of them can interpret
certain resolution instructions. Others interpret their own resolution
instructions. That’s not the point of an IoC, and it’s no wonder they
all do it differently and each has their own strengths and weaknesses.

If we move the registration expression above the container that
performs resolution, we can decouple these two concepts. Then, as long
as the component that understands registration can express instructions
to the component that performs resolution, all containers can suddenly
benefit from these collective strengths. We remove the need for
containers to worry about these concerns. We remove the need for
developers to do an analysis of what “features” a container supports.
We remove the need for people to make trade-offs on what strengths they
gain, what weaknesses they are burdened with and what features they
lose out on when choosing an IoC framework.


The Least Common Denominator

In my brief conversation with Chad, he pointed out that most
abstraction attempts only manage to accomplish the Least Common
Denominator of all frameworks and wind up gutting the potential of each
of the frameworks they integrate with. I understand his point, but I
think this is fundamentally different. The desire is NOT to limit you
to what all frameworks have in common, but to allow people to use
functionality that may NOT exist in all frameworks. A primary example
of this is contextual resolution. Several IoCs can do this to some
extent or another, based on very narrow criteria. Some of them can’t do
it at all. None of them can respond to evolving runtime conditions when
selecting an implementation of a requested type.

When combining these containers with Siege, suddenly all of them
gain this ability. This is because Siege understands both how to
accomplish what you are requesting (it’s specialty is in extensible
registration) and how to communicate with a container to achieve it’s
goal (for example, when condition a is met, request type a. when
condition b is met, request type b). On top of that, it instructs the
container on how to call back to Siege for each dependency that a container has to resolve for additional instructions on how to perform resolution.

This means that by using Siege you can gain functionality that your
selected container doesn’t natively support. And as Siege’s use cases
grow, ALL containers functionality will grow. A rising tide lifts all
boats. If the strengths of each container were built as registration
expressions into Siege, all containers would gain this
strength. You would would sacrifice nothing, make no trade-offs and not
be required to learn multiple distinct ways to express how you want a
type resolved.

 

Limiting your choices

One of the design philosophies behind this project is NOT to limit
your choices. It’s to expand them. If Siege doesn’t support something
that a specific container does, use that container with Siege. Do their
specialized registration to configure the container and then give it to
Siege to do whatever additional registration expressions you need to.
With this approach, we avoid any LCD philosophy, we avoid limiting the
your choices as a developer. Alternatively, you can easily extend the
framework to accomplish what you want so you don’t have to change
containers or feel constrained to a specific one. On top of that, we
deliver functionality that you would not have otherwise. And hopefully,
as the framework grows and evolves, you won’t have to learn 5 different
ways of registering and resolving types. You’ll only have to learn one,
and no matter which underlying framework you use, all of them will
support your system fully. But, that’s down the road from where we are
today, which is why I made it an important design decision to be sure
we didn’t rob any underlying IoC of it’s functionality.

 

Evolution

I’ll be the first to admit, this wasn’t the direction that I was
going when I first started working on this. I wanted to do an 80/20.
But my point of view has evolved over the last month and my efforts
have gone towards supporting the idea that functionality can and will
be introduced to all containers through Siege. Some of the things I’ve
had to say in this post contradict what I’ve said in previous posts
(perhaps I should update them?) But that comes with an evolving point
of view. It’s organic. Things will grow and change. And I want to thank
Chad for challenging me and making me think more in depth about how to
articulate where I’m going and what I’m doing so I could put up a post
like this.

Let each framework specialize and do what it does best. I don’t
expect the existing framework teams to embrace this idea. There will
always be new features introduced to them. There will always be
features in Siege that none of the others will have. That’s the nature
of the beast.

But my goal is to give you more options and to not take any away. I
hope that clears up any confusion around what the intent is here.

 

Happy coding!

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

This entry was posted in IoC, Siege. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://abdullin.com Rinat Abdullin

    I wonder, who cares about decoupling an IoC container from the rest of the code, when they touch only in config and host initialization sections anyway.

  • http://www.lostechies.com/members/mbratton/default.aspx mbratton

    Man, I wish that’s how it was where I work. I can’t tell you the number of references to IKernel and IWindsorContainer that litter our codebase.

  • http://jdon.dev.java.net banq

    > I want them decoupled completely
    My IoC Framework is . include framework itself, please refer:https://jdon.dev.java.net/

    this framework ‘s core is based in picocontainer 1.1, any components can be replaced by xml.