Recently I’ve become pretty enthusiastic about a phenomenon called “Inversion of Control”- or IoC- containers. The hype is blowing over from the J2EE community and is making its way into the .NET community pretty rapidly. IOC could also go by the more imaginative name “plug-in architecture” and is the essence of the .NET ComponentModel implementation.
If the term “Inversion of Control” doesn’t ring a bell and you’re dying to know more, Fowler does a good job explaining it over here.
If you already do know IoC or the ComponentModel, you might get cheated on in this article. I’m doing my best to prove a single point at a time. Trying to explain ‘the whole lot’ in a single sentence (as I usually try to) more than once resulted in puzzled listeners.
One of the main things IoC does is fixing a common OO problem that occurs when a class encapsulates (or inherits from) another class and also has to deal with its dependencies. Please consider the following code snippet that could be part of a LoginComponent:
private ResourceManager _resourceManager;
private EventLogger _eventLogger;
/* ..other encapsulations.. */
public LoginComponent()
{
_resourceManager = new ResourceManager();
_eventLogger = new EventLogger();
//initialisation of encapsulated objects
}
public void DoLogin(string username, string password)
{
//
// login code
//
if (loginUnsuccessfull)
{
string logMessage = _resourceManager.GetString("LOGON_UNSUCCESSFUL_MSG");
_ eventLogger.WriteLogEntry(string.Format(logMessage, username));
}
}
Even though the login code above might be something you wrote plenty of times as a software developer, the possibility of reusing the code is limited.
For the sake of meeting a particular project requirement (the logging of unsuccessful login attempts) the component relies on an implementation of “ResourceManager” and “EventLogger”. The technology used for logging might be your personal favorite (is it Log4NET? EntLib? EIF?) but whichever technology you use, it might conflict with requirements on your next project.
Before I make the obvious comparison to the Factory pattern, I would like to start with showing code using a (IoC-) container. The “Container” class used is a know-it-all factory that serves instances based on configuration settings.
public class DefaultLoginFunctionality: ILoginFunctionality
private ITinyTextFunctionality _resourceManager;
private ILogWriterFunctionality _errorLogger;
public DefaultLoginFunctionality()
{
_resourceManager = (ITinyTextFunctionality) Container.ServeInstance("TinyTextReader");
_errorLogger = (ILogWriterFunctionality) Container.ServeInstance("LogWriter");
}
public void DoLogin(string username, string password)
{
//
// login code
//
if (loginUnsuccessfull && _resourceManager != null && _errorLogger != null)
{
string logMessage = _resourceManager.GetString("LOGON_UNSUCCESSFUL_MSG");
_errorLogger.WriteLogEntry(string.Format(logMessage, username));
}
}
Looking at the code above you should have noticed the following changes:
1.) The names of the “_resourceManager” and “_errorLogger” changed into more functional onces.
2.) The classes became interface definitions.
3.) Initialization of the dependencies is gone.
4.) The code stopped assuming a “_resourceManager” or “_errorLogger” is present and acts accordingly.
The dependency changed from technology to functionality. The functionality is given form by whatever the instance the Container serves.
The Container serves instances specified in configuration. Configuration can be defined per environment (thus per project) and project requirements are met accordingly. The code is therefore reusable in different environments.
The initialization of the served instances is done prior to serving them. Every implementation that can be served by the Container has the ability to initialize itself prior to being served. This initialization is done from configuration private to the served implementation.
Compared to the ‘Factory pattern’ (or AbstractFactory) I think IoC is superior, but I must admit the differences aren’t all that big.
First and foremost, factory patterns use a typed factory and abstact baseclass in their definitions. Every factory is written to serve a specific baseclass and because of this writing a single factory is way more intensive that writing an interface definition.
The trend for factory patterns is that the implementations they serve are bigger and the variety of implementations is less. Within a Container you can easily setup an interface definition that only has a method or two. Using a factory this usually would be too much of a hassle. Would you write a factory & baseclass to serve resource strings?
Besides this main point, when deriving from an abstract baseclass you’re wrecking your implementations inheritance chain. Abstract baseclasses can contain bugs (interfaces cannot). And IoC has a really clear view on context and sharing implementation amongs each other, this is done less with factory implementations.
I’d like to wrap up with having a quick look at the ComponentModel in .net.
The ComponentModel has shown us that different development environments (Borland’s Delphi for .NET & C# Builder, ICSharpCode’s #develop) all managed to show us the same set of controls within their designers.
They did this by serving implementations to a set of interfaces similar to the ISelectionService shown below.
And that; I think is pretty amazing!
public interface ISelectionService
{
// Events
event EventHandler SelectionChanged;
event EventHandler SelectionChanging;
// Methods
bool GetComponentSelected(object component);
ICollection GetSelectedComponents();
void SetSelectedComponents(ICollection components);
void SetSelectedComponents(ICollection components, SelectionTypes selectionType);
// Properties
object PrimarySelection { get; }
int SelectionCount { get; }
}