Powerd by dasBlog RSS 2.0
 Thursday, December 27, 2007

Inversion of Control (IoC) and Dependency Injection (DI) are two practices, patterns, techniques, that I have been very interested to take a look at in greater detail. I came across a tutorial (the tutorial) which goes over IoC and DI using the Castle Windsor Container (and Micro Container), part of the Castle Project. Two other similar implementations presented in the tutorial are Spring.NET and StructureMap, as well as Microsoft's ObjectBuidler DI framework. The ObjectBuilder framework is used in the Enterprise Library, and Composite UI Application Block (CAB), which is good enough for me to steer clear of it and try out the Windsor Container instead.

I have never really been impressed with the Enterprise Library, and the CAB seemed overly cumbersome. Granted, it has been over a year since I last looked at the CAB and EntLib in any great detail, so maybe things have gotten better. I know allot of people live and breath EntLib, but I'm trying to experience some other thoughts outside the typical Microsoft sphere, such as Alt.Net. Microsoft is starting to catch onto the Alt.Net movement especially with the introduction of the Asp.Net MVC framework, a topic for a future post.

Part I

So what exactly is inversion of control? It helps if you consider how an application is traditionally written and define what "control" means. "Control" can be thought of as adding references to various assemblies, declaring instances of specific specific classes by calling the appropriate constructor and passing in parameters. The tutorial refers to this as a "canonical programming process". Now if we take the opposite, or invert the control, then our application does not reference specifically assemblies and does not instantiate concrete classes. Instead your application makes use of interfaces or abstract classes and relies on a framework to instantiate the classes as needed. This is commonly referred to as the Hollywood Principle, and what post on design patterns would be complete without a link to Martin Fowler.

The tutorial talks about another design principle, which is the separation of concerns (SoC). Separation of Concerns states that a component should do only one task, and do it very well. In the tutorial's example application, the functionality to download HTML data should be separated from the logic to parse the HTML itself. By separating the two pieces and applying IoC, if a new, faster algorithm for parsing was written, it could be swapped out with the original with no modification to the application. Under a traditional model, you would have to edit the single class, change the parsing routine, and then recompile the class and application. Then what happens when management says, "well the first algorithm was slower, but it was written by the boss's nephew....".

The tutorial defines a component as: "... a small unit of reusable code. It should implement and expose just one service, and do it well. In practical terms, a component is a class that implements a service (interface). The interface is the contract of the service, which creates an abstraction layer so you can replace the service implementation without effort."

The power of an Inversion of Control framework does not become apparent until you have components with dependencies on other components, each with their own dependency. The Castle project refers to it's IoC as a container which is offered in two versions. The first exposes the core IoC functionality and is called the MicroKernel. In the simplest of terms, you add components by specifying a key (ID), type and assembly to load. You then ask the container for an instance, which it returns, loading all required dependencies. The 2nd version of the container, Windsor, extends the Micro Kernel and is used most often. It adds supports for generics to reduce casting and external configuration (app.config and web.config) options to name a few.

Part II:

  • Constructor, Setter, and Component Injection
    • Constructor arguments, and [property] setter arguments can be specified in the configuration file.
    • If more then one component of a service is specified, you can specify which component to use, using the ${ComponentName} syntax in the configuration file.
    • The name of the XML Elements in the configuration file should match a constructor parameter and property name.
  • Collections in the configuration file
    • Arrays, Dictionaries and Lists can be specified in the configuration file.
    • This allows you to pass in a collection of components and allow the parent container to decide which one to use.
    • Collections can be specified for constructor parameters and property setters.

Part III

  • Separation of configuration and data
    • The configuration file can get complex pretty fast, especially when you are specifying a large set of data for a collection.
    • Windsor allows you to work around this by moving the data to the top of the configuration file under a <properties /> section. Child elements can be referenced in the configuration section by using the #{ChildPropertyName}
    • The tutorial points out that the real power of this, lies in the fact that you can put the properties in a separate file.
  • Custom Converters
    • Windsor supports converting from strings (in the configuration file) to other types.
    • You need to implement AbstractTypeConvertor, and a new class inheriting from WindsorContainer to register your TypeConvertor
  • Decorator Pattern
    • The decorator pattern basically has you wrapping a component that implements IInterface, with another component that implements IInterface, taking the original component as a constructor parameter.
    • Once you have the new implementation, you can perform things like benchmarking around specific method calls.
    • Since everything is setup via the configuration file(s), there was no need to re-compile the main container.
  • Define and If
    • Windsor supports an IF/Else syntax in the configuration file
    • Windsor also supports using DEFINED variables, such as DEBUG. This is not the same as the compiler flags, you have to add your own DEFINE statements in the configuration file. this can be in either the main component configuration, or your properties configuration.

Party IV

  • Lifestyles
    • Windsor supports 4 object lifecycles
      • Singleton (Default) - Each call to Resolve returns the same instance
      • Transient - Each call to Resolve returns a new reference
      • Thread - Each thread gets a single instance
      • Pooled - Instances are retrieved from a pool. If there are no more available instances in the pool, a new one is created. All instances are created upon the container being instantiated. 
      • Custom
    • Specify the lifestyle using attributes (i.e. [Transient]), or via the configuration file
  • Facilities
    • From the official documentation (taken from the tutorial): Facilities augment the MicroKernel capabilities by integrating it with a different project or technology, or by implementing new semantics.
    • Built in facilities (Covered by the tutorial):
      • Factory - Allows you to use classes which implement an Interface but use the factory pattern
      • Startable - Components are "started" as soon as possible after the application is started, and stop when it's released. You can implement an IStartable interface, or, specify start and stop methods in the configuration file.
    • The logging facility is mentioned as one being worth while to investigate, but it's not covered.
  • Commission and Decommission
    • Implement the ICommission and IDecommission to specify code which should be run when the component is created and destroyed by the container. Depending on the lifestyle chosen, the actual time at which the decommission code is called varies, and without understanding this, you may get unexpected results.
  • IDisposable
    • If you implement IDispoable, the container will automatically call the dispose method when the component is no longer needed. 

Questions:

  • Is there a utility that can analyze an assembly, namespace and/or class and generate a skeleton configuration file?
    • The StructureMap lists some tools that make configuration file management easier.
  • Other ways to store configuration data. It would be great if you could load configuration, or the properties from a SQL data store.
    • StructureMap supports full programmatic configuration.

Conclusion:

I can't wait to try out the Micro Kernel and Windsor Container in a project. The flexibility is phenomenal, and the decorator pattern makes it extremely easy to extend functionality.  I only took a quick look at the StructureMap and Spring.Net, and they have some interesting features which need further investigation.

Thursday, December 27, 2007 4:10:24 AM UTC  #    Comments [0] - Trackback
Programming
Archive
<December 2007>
SunMonTueWedThuFriSat
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2008
Adam Salvo
Sign In
Statistics
Total Posts: 160
This Year: 86
This Month: 2
This Week: 0
Comments: 9
Themes
All Content © 2008, Adam Salvo
DasBlog theme 'Business' created by Christoph De Baene (delarou)