In this article I conclude the discussion about the core features of Windsor Container while evolving the
simple example that accompanied you through the whole series of these articles. The next article will tackle more
advanced topics.
Inversion of Control/Dependency Injection Series
IoC/DI - Part I This article introduces Inversion of Control/Dependency Injection in a simple and
affordable fashion, with a real example which evolves step by step to take advantage from IoC and DI, leveraging
the features offered by an open source framework called Castle Project.
IoC/DI - Part II In the previous article I
introduced the concepts of Inversion of Control and Dependency Injection, and showed
how to benefit from their use when developing a simple application. In this article
I will resume the previous discussion extending the requirements of the former example
to show how IoC deals with changes and what is its real potential.
IoC/DI - Part III In this article I conclude the discussion about the core features of Windsor
Container while evolving the simple example that accompanied you through the whole series of these articles. The
next article will tackle more advanced topics.
IoC/DI - Part IV In this article I introduce the missing core features I didn't tackle before. They require a little more knowledge of Windsor, which I hope I've been able to hand down so far.
Introduction
So far I've discussed and presented some simple features available in Windsor. It exposes many more
configuration options, not just concerning the way you can inject dependencies, but even how to control their
behavior. As usual, I will explain the remaining set of core features and some advanced ones by introducing small
incremental changes in the requirements of the sample application.
By now, the sample application is capable of downloading files via a couple of protocols - HTTP and FTP - and
can easily be extended to support any other, just by following these simple steps:
- Create a component exposing the
IFileDownloader service.
- Register it into the Windsor configuration file.
- Add it to the array/list/dictionary of accepted components as a constructor-mandatory dependency of
the
HtmlTitleRetriever class.
Just as a refresher, let's create a new implementation of the service that is able to retrieve files from
the file system.
Note: Uris referencing files on the local file system have the Scheme property set to "file".
The snippet below shows the code for the FileSystemFileDownloader class.
Obviously, the component has to be registered into the configuration file and added to the array of
downloaders accepted by the constructor of the HtmlTitleRetriever class.
Now let's change the requirements of the application and introduce new concepts.
Configuration properties and includes
The new requirement specifies that there are three files to be downloaded; one via FTP, one via HTTP and one
from the file system. These files are mostly static but shouldn't be hard coded since they may eventually change.
The choice is to specify them via configuration file and edit the code of the HtmlTitleRetriever
class to enable it to accept the Uris of the files either as a compulsory or optional dependency.
Let's think about the requirement for a while. Should you make the files an optional or mandatory dependency?
Will you supply them via an array, a list or a dictionary? In this case it's just a matter of taste. If you opt
for the compulsory dependency then you will supply them as a constructor parameter and you're done. Otherwise you
will use a read/write property with a backing field initialized to an empty array/list/dictionary, to avoid null
references in case no parameter is specified. Let's go the optional dependency way, so that the code of the class
becomes the one shown below.
Other than the new Files property, a new method called GetTitles has been
added. It doesn't accept any input parameters and simply iterates through the array of files, calling the
GetTitle method on each of them. The rest of the code is unchanged. These changes have to be
reflected in the configuration file as well. The related section becomes as follows:
Note that I chose a mirror of the Garr to download
files because it makes them available both via HTTP and FTP. The other file is a file I created locally.
Now, a little problem arises. You can see that along with complexity and requirements, the verbosity of the
configuration file increases. In particular, static information like component registration is mixed up with more
variable stuff, like AdditionalMessage and Files dependencies. It may be desirable to
have them separated from the other stuff, so that they can be changed easily without fiddling into the whole
configuration file. Windsor offers a feature called configuration properties which fits exactly this need.
By using configuration properties you can create an indirection mechanism that lets you specify references
values in another section of the configuration file. The syntax for configuration properties is
#{property name}. This doesn't require any change in the code of the classes. The
changes to the configuration file are shown in the following snippet.
A new child section called properties has been added inside the castle section. It contains the
values that appeared directly in the components declarations. These values are referenced by the
HtmlTitleRetriever component using the syntax introduced before.
Now, this wouldn't be such an advantage if you couldn't really separate the properties and components sections.
If you needed to deploy the application several times, the only changes may involve the properties section, while
the components declaration would remain mostly unchanged. For this purpose Windsor lets you use several
configuration files, which are inspected and merged at startup. This is achieved via includes. Includes support
loading configuration files from several mediums such as the file system, a network share or an assembly resource.
The snippet below shows what the App.config file looks like when using includes. Note that the
Properties.config is loaded from the file system, using a path relative to the executable of the application,
while the Components.config file is loaded from the assembly.
Below is reported the Properties.config file. Note that in order for the application to be able to
retrieve it, it needs to be placed in the same directory as the app executable file. Under Visual Studio, this is
accomplished by setting the file's Copy to Output Directory to Always or Copy if newer.
I commented out the file retrieved from the file system to avoid forcing you to create one when
running the sample application. The source code is available for download.
Finally, the snippet below shows the Components.config file. Since it is retrieved as an assembly resource, its
Build Action property must be set to Embedded Resource.
Using includes you can separate static configuration information from volatile one, and ease its
lookup and editing.
Custom type converters
If you run the sample application as is, you will run into an exception of type ConverterException
saying "No converter registered to handle the type System.Uri". Windsor Container, in fact, can convert your
configuration parameters from strings to the destination type as long as they are of simple type. The conversions
supported out of the box by the container are listed in this documentation page.
Actually, the Files dependency of the HtmlTitleRetriever component is of type
Uri[]. Arrays are supported, but Uris aren't. However, Castle provides a not very straightforward API
to supply custom converters, and this requires a bit of work.
First, a class inheriting from the abstract AbstractConverter class needs to be created, which
performs the conversion from string to Uri. This is trivial, as shown in the following snippet.
Then, and here comes the tricky part, you need to inform the container about it. The suggested
approach is to create a class inheriting from WindsorContainer and perform the type converter
registration in the constructor. Here's the code of the CustomContainer class.
Now the sample application instantiates a CustomContainer instead of a
WindsorContainer and everything works as expected.
Decorators
Decorator is a design
pattern formalized for the first time in the book Design Patterns: Elements of reusable object oriented software.
If you are not familiar with it, you just need to know that it decorates an object by adding features to it,
without changing the original class. IoC lets you benefit of this pattern in a very straightforward way. First,
let's introduce a new requirement for the sample application. Let's talk about the parsing mechanism.
So far, you've extracted the title information from the contents of the HTML file using string functions. You
may be wondering whether this is more or less performance-wise. I did, and I tried to think about another
mechanism for parsing the file. This obviously entails creating a new class implementing the
ITitleScraper interface.
There are a lot of HTML parsers for .NET, but for this simple application you aren't going to need them.
Instead, I chose to take the regular expressions route. So here's the code for the RegexTitleScraper
class, which hopefully will perform better than the StringParsingTitleScraper class.
Now you can switch from the old scraping component to the new one by registering it in the
configuration file and specifying it in the configuration section of the HtmlTitleRetriever
component.
How do I know that using regular expressions the parsing is faster than using strings? I'd need a
benchmarking mechanism. Using the StopWatch class you can measure with a good precision the time
taken by a certain operation. Doing this requires changing the code of both the scraping classes. Once it is
determined which one is faster, you remove the benchmarking code and tell Windsor to use the faster one. But you
don't want to do this, since there might be a better way. In fact there is, and it consists in applying the
decorator pattern. I've created a BenchmarkingTitleScraperDecorator class which does exactly this.
The class implements the ITitleScraper interface. Therefore, it's interchangeable with
the other scrapers seen so far. The difference is that this one doesn't perform any scraping. Instead, it
delegates the operation to its inner scraper, supplied as a constructor parameter. This is just the logic behind
the decorator pattern. Now let's see how to take advantage of it using Windsor.
You can see that a chain has been created. HtmlTitleRetriever is supplied a
BenchmarkingTitleScraperDecorator, which in turn takes a StringParsingTitleScraper as a
constructor dependency. This simple mechanism lets you decorate the parsers with benchmarking capabilities, in
order to measure their performance. Just switch to RegexTitleScraper to measure its performance and
compare it with the other parser. Once done, supply the best scraper directly to the
HtmlTitleRetriever component and trash away the benchmarker and the other scraper. Easy, isn't it? As
a matter of fact, on my machine the string parser outperforms the regular expressions.
Defines and ifs
Another feature which might come useful in many cases is the ability to define flags similar to the
preprocessor directives you use in .NET code. Windsor can understand defines and ifs, which follow the syntax
shown below in the configuration file. They can be employed to register components conditionally, or to choose
which component to instantiate based on a flag. In the sample application, I will use them to switch between the
BenchmarkingTitleScraperDecorator and a real implementation of a scraper so that it becomes easier to
do benchmarking tests during debug and then go back to production mode with no benchmarking. Here's how the
configuration for the main component changes to benefit of this feature.
As you can guess from the code above, the HtmlTitleRetriever component will use the
benchmarking parser when a DEBUG flag is defined; the string parser otherwise. I will define and undefine that
flag in the main configuration file for the application, making it easy to switch from debug to production mode
and vice versa.
To switch mode you just need to comment out the define. Defines and ifs can be used with no
restrictions on their location within the configuration files, with the only constrain that ifs can't be nested.
Summary
In this article you've seen how Windsor lets you isolate volatile configuration parameters into a specific
section of the configuration file. Then, I talked about includes and how they help separating configuration files
to achieve a higher modularity during configuration and deployment. Next, you've seen how to deal with parameter
types not directly supported by Windsor, by implementing custom type converters and using them by subclassing the
WindsorContainer class. Finally, I've presented one of my favorite features whose implementation is
extremely simplified by Windsor: The decorator pattern and how easy is to switch between implementations using ifs
and defines. In the next article I'll talk about factories, lifecycles and lifestyles.
References
Please login to rate or to leave a comment.