Los Techies : Blogs about software and anything tech!

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!

Kick It on DotNetKicks.com
Posted Jan 10 2010, 04:19 PM by mbratton
Filed under: ,

Comments

Rinat Abdullin wrote re: A philosophical discussion about Inversion of Control frameworks
on 01-11-2010 12:14 PM

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.

mbratton wrote re: A philosophical discussion about Inversion of Control frameworks
on 01-11-2010 12:34 PM

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.

banq wrote re: A philosophical discussion about Inversion of Control frameworks
on 01-14-2010 10:38 PM

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

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems