Monday, March 30, 2009

Unleashing modules

I’m a big fan of the Autofac IoC-container, it’s pretty light weight, blazingly fast and very object oriented. One of the features I love the most is the ability to divide the boot strapping code into modules. Also, to set up your dependencies in code with a fluent interface rather than in an xml-file is great in so many ways (intellisense and refactoring to name a few). The one problem I’ve found with this is that it couples your assemblies tighter than they might need to be coupled.

The problem

Let’s consider this example: We have an application that makes use of the service IFoo, this interface is defined in the assembly FooAssembly. There is an implementation of the IFoo-interface called XmlFoo in the XmlFooAssembly and this implementation is the implementation we want to use in our application. The implementer of the XmlFooAssembly has been nice enough to provide a module that registers the XmlFoo as an IFoo and also registers services that the XmlFoo-implementation itself is dependent upon. This is quite nice since now, in our boot strapping code what we have to do is to register that module along with any other registrations we have, something like this:

var builder = new ContainerBuilder();

builder.RegisterModule(new MyApplicationDependencyModule());
builder.RegisterModule(new XmlFooDependencyModule());

var container = builder.Build();

 

This is all pretty nice and clean, not much of a problem right? Well, there is one problem with this, in order for us to register the XmlFooDependencyModule we have to have a reference to the assembly where it’s defined (XmlFooAssembly) from our application assembly, note that this is the only place we need this reference, in all other places we’re programming against the abstraction IFoo, not the XmlFoo. This couples the assemblies more tightly than they need to be. It’s not that this is a giant problem, and it can be solved by using xml-configuration of dependencies rather than using the module directly, however, another problem with this is that if we’re using the same dependencies in multiple applications we have a lot of boiler plate code in that is duplicated along applications.

Your going to fix this are you?

Well, yeah, there is a point with this blog post. I guess you’re familiar with MEF (Managed Extensibility Framework), if not go read up on it, it’s a very cool technology. My idea is that we could use MEF to export the modules from the assemblies that implement them and import them in the application that uses them, this way we can remove the reference to the XmlFooAssembly. Instead of registering the modules we know exists, we register all modules that are “magically” imported. To do this I created a new module type that inherits the Autofac.Builder.Module-class and is called ComposedModule. You can for example instantiate the module with a path to a directory that contains a number of assemblies, the composed module will then (through MEF) find all the exported modules in the assemblies in the folder. When you register this composed module in your builder it will register all the imported modules in the builder:

var builder = new ContainerBuilder();

builder.RegisterModule(new ComposedModule(@"c:\MyApplicationDirectory"));

var container = builder.Build();

 

You could actually give the ComposedModule a MEF-catalog instead of the directory path and the ComposedModule will use this catalog to load the exported modules:

var builder = new ContainerBuilder();

builder.RegisterModule(new ComposedModule(new AssemblyCatalog(this.GetType().Assembly)));

var container = builder.Build();

A blog post about the implementation details and some other features of the ComposedModule is coming in the next few days so please check back for that.

Sunday, March 8, 2009

If If Then Throw New CodeSmellException()

I’ve had this – somewhat controversial – opinion for a long time, that if you have an if-statement there’s something wrong with your design. Of course this is taking it a bit too far, but I think that the initial reaction for any developer that finds her-/himself writing an if-statement should be “could I do this differently”.

The last week I’ve been leading a new team and we’re starting a new project and we’ve been building a prototype for the product we’re going to build. It’s an API intended for internal use and I can’t talk to much about it but it’s not a trivial piece of software. It handles going out to external systems, aggregating answers from them, displaying the information in a UI and let’s the user communicate back to the external systems with additional information. The prototype has maybe something like twenty classes and a couple of hundred lines of code, now here’s the point of this whole post: this Thursday when at least 90% of that code was already written we wrote our first if-statement. I called it out “there it is!”. The other developers were like: “what?”. “-That’s our first if-statement”. The prototype is done and there is still one single if-statement in it, and it’s not that we’ve been trying to avoid it, we’ve just designed with the SOLID-principles in mind which leads to a more sound and less complex architecture.

Thursday, February 26, 2009

Fluent interface for argument validation

I was fiddling around a bit with a fluent interface for validating method-arguments, it looks kind of promising…

    Public Class Foo
        Public Sub New(ByVal bar As String, ByVal baz As Integer)
            Ensure.ThatArgument(bar).IsNotNull()
            Ensure.ThatArgument(baz).IsInRange(1, 10)

            ' The argument can be named to for better exception messages...
            Ensure.ThatArgument(bar).Named("bar").IsNotNull()
            Ensure.ThatArgument(baz).Named("baz").IsInRange(1, 10)
        End Sub
    End Class

Using generics also makes the intellisense really helpful, for example the “IsInRange”-validation only shows up for IComparable(T)-types.

Thursday, February 12, 2009

String concatenation made easy

How many times have you written code similar to this?

   1: var builder = new StringBuilder();
   2:  
   3: foreach (string foo in bar)
   4: {
   5:     builder.Append(foo);    
   6: }
   7:  
   8: return builder.ToString();

I have hopefully done it for the last time since I created a couple of extension methods for IEnumerable<string>, well actually, one method with a couple of overloads.

The first one is really simple and just concatenates all the strings in the specified collection to a single string, something like:

   1: var sequence = new string[] { "foo", "bar" };
   2: string result = sequence.Concatenate();

In this case the result variable will contain the string “foobar”. One of the overloads will let you specify a separator:

   1: var sequence = new string[] { "foo", "bar" };
   2: string result = sequence.Concatenate(", ");

Now the result is “foo, bar”.

And the last one will let you specify a prefix and a suffix in addition to the separator:

   1: var sequence = new string[] { "foo", "bar" };
   2: string result = sequence.Concatenate(", ", "(", ")");

Giving you the result “(foo, bar)”. Quite neat!

Here’s the code for the extensions:

   1: /// <summary>
   2: /// Concatenates all the strings in the specified sequence.
   3: /// </summary>
   4: /// <param name="values">The values to concatenate.</param>
   5: /// <returns>The concatenated sequence.</returns>
   6: public static string Concatenate(this IEnumerable<string> values)
   7: {
   8:     return values.Concatenate(string.Empty);
   9: }
  10:  
  11: /// <summary>
  12: /// Concatenates all the strings in the specified sequence, separated by
  13: /// the specified separator.
  14: /// </summary>
  15: /// <param name="values">The values to concatenate.</param>
  16: /// <param name="separator">A string that will be inserted between all the values
  17: /// in the resulting string.</param>
  18: /// <returns>The concatenated sequence, separated by
  19: /// the specified separator.</returns>
  20: public static string Concatenate(this IEnumerable<string> values, string separator)
  21: {
  22:     return values.Concatenate(separator, string.Empty, string.Empty);
  23: }
  24:  
  25: /// <summary>
  26: /// Concatenates all the strings in the specified sequence, separated by
  27: /// the specified separator, prefixed by the value specified in <paramref name="prefix" /> and
  28: /// suffixed by the value specified in <paramref name="suffix"/>.
  29: /// </summary>
  30: /// <param name="values">The values to concatenate.</param>
  31: /// <param name="separator">A string that will be inserted between all the values
  32: /// in the resulting string.</param>
  33: /// <param name="prefix">A string that will be the start of the result string.</param>
  34: /// <param name="suffix">A string that will be the end of the result string.</param>
  35: /// <returns>The concatenated sequence, separated by
  36: /// the specified separator, prefixed by the value specified in <paramref name="prefix" /> and
  37: /// suffixed by the value specified in <paramref name="suffix"/>.</returns>
  38: public static string Concatenate(this IEnumerable<string> values, string separator, string prefix, string suffix)
  39: {
  40:     Guard.ArgumentNullException(values, "values");
  41:     Guard.ArgumentNullException(separator, "separator");
  42:     Guard.ArgumentNullException(prefix, "prefix");
  43:     Guard.ArgumentNullException(suffix, "suffix");
  44:  
  45:     var result = new StringBuilder();
  46:  
  47:     result.Append(prefix);
  48:  
  49:     foreach (var value in values)
  50:     {
  51:         if (result.Length > prefix.Length)
  52:         {
  53:             result.Append(separator);
  54:         }
  55:  
  56:         result.Append(value);
  57:     }
  58:  
  59:     result.Append(suffix);
  60:  
  61:     return result.ToString();
  62: }

… and some NUnit-tests:

   1: [TestFixture]
   2: public class sequence_of_strings_Concatenate
   3: {
   4:     string[] sequence;
   5:  
   6:     [SetUp]
   7:     public void SetUp()
   8:     {
   9:         this.OnSetUp();
  10:     }
  11:  
  12:     protected virtual void OnSetUp()
  13:     {
  14:         sequence = new string[] { "a", "b", "c" };
  15:     }
  16:  
  17:     [Test, ExpectedException(typeof(ArgumentNullException))]
  18:     public void should_throw_exception_when_sequence_is_null()
  19:     {
  20:         Legend.Collections.Collection.Concatenate(null);
  21:     }
  22:  
  23:     [Test]
  24:     public void should_concatenate_values()
  25:     {
  26:         var result = sequence.Concatenate();
  27:         Assert.AreEqual(result, "abc");
  28:     }
  29: }
  30:  
  31: [TestFixture]
  32: public class sequence_of_strings_Concatenate_with_specified_separator
  33: {
  34:     string[] sequence;
  35:  
  36:     [SetUp]
  37:     public void SetUp()
  38:     {
  39:         this.OnSetUp();
  40:     }
  41:  
  42:     protected virtual void OnSetUp()
  43:     {
  44:         sequence = new string[] { "a", "b", "c" };
  45:     }
  46:  
  47:     [Test, ExpectedException(typeof(ArgumentNullException))]
  48:     public void should_throw_exception_when_sequence_is_null()
  49:     {
  50:         Legend.Collections.Collection.Concatenate(null, ",");
  51:     }
  52:  
  53:     [Test, ExpectedException(typeof(ArgumentNullException))]
  54:     public void should_throw_exception_when_separator_is_null()
  55:     {
  56:         sequence.Concatenate(null);
  57:     }
  58:  
  59:     [Test]
  60:     public void should_concatenate_values_separated_by_separator()
  61:     {
  62:         var result = sequence.Concatenate(", ");
  63:         Assert.AreEqual(result, "a, b, c");
  64:     }
  65: }
  66:  
  67: [TestFixture]
  68: public class sequence_of_strings_Concatenate_with_specified_separator_prefix_and_suffix
  69: {
  70:     string[] sequence;
  71:  
  72:     [SetUp]
  73:     public void SetUp()
  74:     {
  75:         this.OnSetUp();
  76:     }
  77:  
  78:     protected virtual void OnSetUp()
  79:     {
  80:         sequence = new string[] { "a", "b", "c" };
  81:     }
  82:  
  83:     [Test, ExpectedException(typeof(ArgumentNullException))]
  84:     public void should_throw_exception_when_sequence_is_null()
  85:     {
  86:         Legend.Collections.Collection.Concatenate(null, ",", string.Empty, string.Empty);
  87:     }
  88:  
  89:     [Test, ExpectedException(typeof(ArgumentNullException))]
  90:     public void should_throw_exception_when_separator_is_null()
  91:     {
  92:         sequence.Concatenate(null, string.Empty, string.Empty);
  93:     }
  94:  
  95:     [Test, ExpectedException(typeof(ArgumentNullException))]
  96:     public void should_throw_exception_when_prefix_is_null()
  97:     {
  98:         sequence.Concatenate(string.Empty, null, string.Empty);
  99:     }
 100:  
 101:     [Test, ExpectedException(typeof(ArgumentNullException))]
 102:     public void should_throw_exception_when_suffix_is_null()
 103:     {
 104:         sequence.Concatenate(string.Empty, string.Empty, null);
 105:     }
 106:  
 107:     [Test]
 108:     public void should_concatenate_string_with_separator_prefix_and_suffix_appended()
 109:     {
 110:         var result = sequence.Concatenate(", ", "(", ")");
 111:         Assert.AreEqual("(a, b, c)", result);
 112:     }
 113: }