Dependency Injections and Inversion of Control Containers -- Chicago Architects Group January Meeting Recap

(Each month I plan to attend technical user groups in the Chicago area to (re-)learn from peers experiences with new and existing technologies and to network with like-minded techies. This blog is one in a series of recaps of some of the more interesting aspects of the meetings for my own purposes (this is a “web log” afterall) and for others to get a general taste of what’s available in the Chicago user group scene.)

User Group: Chicago Architects Group

http://www.chicagoarchitectsgroup.com/

Location: ITA (200 S Wacker - next to Sears Tower)

Meeting Date: Tuesday, January 19th, 2010

The CAG meetings I’ve been to have had between 10 and 20 attendees and tend to be more of the Architect/Team Lead types and less of the hard-core, in your face techies/developers (this is not a bad thing, in my opinion). Like most user groups, they have pizza and soda as well as giveaways at the end (today included Windows 7 Ultimate, Office 2007, a wireless mouse and several books). Personally, I find the meetings to be useful, although I know some of my coworkers who have come with me will likely not attend unless the topics are of great personal interest to them.

Presenter: Tim Murphy

Blog: http://geekswithblogs.net/tmurphy

Twitter: @twmurph

Topic: Dependency Injection and IOC Containers

I'll admit up front that this entry will be a little light on content, such as the pros/cons to using this pattern – mainly I’m making notes for me to reference later. Here is a link to the presenter’s wrap-up as well, with slides and code (using Unity and Windsor). http://geekswithblogs.net/tmurphy/archive/2010/01/19/cag-january-2010-wrap-up.aspx

But by all means, please read on :-)

The Dependency Injection pattern is intended to lessen the coupling of objects by having dependant objects provided to an object instead of having the object itself generate the dependant objects. For example, you have a Widget object which depends upon a LoggingManager object and a WidgetValidator object. Without Dependency Injection, you might instantiate your dependant objects insider your object constructor, like this:

namespace WrightThisBlog.blogspot.com
{
    public static class MainApp2
    {
        public static void main(object[] argv,  int  argc)
        {
            var  myWidget =  new Widget ();
            //do some widgetity stuff here
        }
    }
    
    public interface IWidget  {}
    
    public interface ILogger  { }
    
    public interface IValidator  { }
    
    public class LoggingManager  :  ILogger { }
    
    public class WidgetValidator  :  IValidator { }
    
    public class Widget :  IWidget
    {
        ILogger  _logger;
        IValidator  _validator;
        
        public  Widget()
        {
            _logger =  new LoggingManager ();
            _validator =  new WidgetValidator ();
        }
    }
}

But now you're tied to a specific implementation of ILogger and IValidator and if you ever wanted to change them, you'd have to modify your code anywhere you referenced those objects and replace them with your new ILogger object.   This is less than ideal and makes your code a bit fragile.

So how does Dependency Injection change this?   Basically, by having the caller provide the ILogger and IValidator to the Widget object, either as parameters in the constructor or as properties. This is further decoupled in my example by using factory classes:

namespace   WrightThisBlog.blogspot.com
{
    public static class MainApp2
    {
        public static void  main( object [] argv,  int  argc)
        {
            IWidget  myWidget =  WidgetFactory.GetWidgetInstanceViaConstructor();
            IWidget  myNextWidget =  WidgetFactory.GetWidgetInstanceViaProperties();
            //do some widgetity stuff here
        }
    }
    
    public class ImprovedWidget :  IWidget
    {
        public  ImprovedWidget( ILogger loggerToUse,  IValidator  validatorToUse)
        {
            LoggerToUse = loggerToUse;
            ValidatorToUse = validatorToUse;
        }
        
        public  ImprovedWidget() {}
        
        public ILogger LoggerToUse {  get ;  set ; }
        public IValidator ValidatorToUse {  get ;  set ; }
    }
    
    public static class WidgetFactory
    {
        ///<summary>
        ///Could provide in constructor
        ///</summary>
        public static IWidget  GetWidgetInstanceViaConstructor()
        {
            ILogger  logger =  LoggerManagerFactory.GetLoggerInstance();
            IValidator  validator =  WidgetValidatorFactory.GetValidatorInstance();
            return new ImprovedWidget (logger, validator);
        }
        
        ///<summary>
        ///Or via properties (beware unset properites!)
        ///</summary>
        public static IWidget  GetWidgetInstanceViaProperties()
        {
            return new ImprovedWidget
                {
                    LoggerToUse =  LoggerManagerFactory.GetLoggerInstance(),
                    ValidatorToUse =  WidgetValidatorFactory.GetValidatorInstance()
                };
        }
    }
        
    public static class LoggerManagerFactory
    {
        public static ILogger  GetLoggerInstance() {  return new LoggingManager (); }
    }
    
    public static class WidgetValidatorFactory
    {
        public static IValidator  GetValidatorInstance() {  return new WidgetValidator (); }
    }
}

Now you’ve created a loose coupling between your Widget and its supporting objects, which will come in handy when

  • You want to use a mocking tool (like RhinoMocks) to unit test your code
  • You want to replace your LoggingManager with something else that implements ILogger – now you only need to update the factory classes (technically, using a factory class is a different pattern, but works well here)

Still, you’re coding in a concrete implementation in your factory, so while you’ve isolated the number of places you need to update, you’re still hard-coding in an implementation.   Additionally, if there are very deep dependencies (you’re Logger needs a FileManager which needs a PermissionsManager which needs a CurrentUserManager….), this can get pretty ugly to manage and you end up writing a lot of plumbing code that is only tangential to the application’s real purpose.

This is where the IOC Containers come into play.   Using an IOC framework, you define which concrete classes implement your interfaces and what dependencies they have.   Then you use the IOC container like a factory class to instantiate your objects.   There are two primary means for defining the dependency trees: using XML in your app.config or via code.   Per the group discussions, some IOC frameworks provide tools (such as Structure Map) which will auto-generate your code mappings, while others (like Microsoft’s Unity Framework) use [Dependency] attributes to denote where there are dependencies.   This allows you to say “I need an IWidget” and the container framework will know that ImprovedWidget should be created and it has dependences on ILogger and IValidator, which are provided by LoggerMananger and WidgetValidator, and so on.

using   Some.IOC.Framework;

namespace   WrightThisBlog.blogspot.com
{
    public static class MainApp3
    {
        public static void  main( object [] argv,  int  argc)
        {
            var  IOC_container = IOC_Framework.GetContainer();
            IWidget  myWidget = IOC_container.GetObject<IWidget>();
            //do some widgetity stuff here
        }
    }
}

Resources and Reference:

Some IOC Frameworks:

  • Structure Map
    • Has “scanning” tool to auto-map dependencies and limit the amount of manual configuration needed. (If only one class in your project implements a given interface, that class will be used when that interface is requested).
  • Ninject
  • Castle Windsor
    • Uses app.config to defined assemblies/classes to use for each interface.    Uses constructor to provide dependencies.
  • Microsoft Unity (part of P & P group)
    • Uses [Dependency] attributes places on class properties to determine where dependencies are needed, as well as to get/set them.

Some links: