Chain of Responsibility Pattern - Builder Pattern - Fluent Interfaces

Chain of Responsibility Pattern - Builder Pattern - Fluent Interfaces

by David Hayden ( Microsoft MVP C# ), Filed: Design Patterns

 

I have built a Dependency Injection Application Block to show off the Application Block Software Factory in Enterprise Library 3.1. It has an Enterprise Library Design-Time Experience as such:

 

dependency injection application block

 

and a runtime experience with the following API:

 

ObjectFactory.Initialize();
ObjectFactory.Initialize(IConfigurationSource configurationSource);
ObjectFactory.Create<T>();
ObjectFactory.Add<TInterface,TConcrete>();
ObjectFactory.Remove<T>();
ObjectFactory.Clear();

 

If I have a class as follows:

 

public class ProductCatalog : IProductCatalog
{
    private readonly IProductRepository _productRepository;
    private readonly ICategoryRepository _categoryRepository;

    public ProductCatalog() { /* ... */ }

    [InjectionConstructor]
    public ProductCatalog(IProductRepository productRepository,
                ICategoryRepository categoryRepository)
    {
        _productRepository = productRepository;
        _categoryRepository = categoryRepository;
    }
}

 

The Dependency Injection Application Block ( DIAB ) will automatically inject the product and category repositories into the Product Catalog for you and all you have to write is this:

 

IProductCatalog productCatalog =
          ObjectFactory.Create<IProductCatalog>();

 

Finding The Injection Constructor

One of many things to think about when creating such a block is which constructor to use to create an object. The DIAB has a strategy for determining the appropriate constructor to inject dependencies into, but this strategy can be changed at will. Currently I do the following:

  1. Use the constructor with the attribute [InjectionConstructor] if it exists.
  2. Use the constructor with the most parameters that can be satisfied by types specified in the container.
  3. ...

There is a sequence to this. If #1 can't happen, go to #2. If #2 can't happen, go to #3. This is actually the Chain of Responsibility Pattern and can be visualized like this in my code:

 

Chain of Reponsibility Pattern

 

The idea here is that there is a pipeline of IConstructorFinderStrategies with each strategy responsible for calling the next if it can't find an appropriate constructor. I currently have 2 strategies named as follows and focusing on the rules mentioned above:

  • FindConstructorWithInjectionConstructorAttributeStrategy.cs
  • FindConstructorWithMostParametersSatisfiedStrategy.cs

My class names get a little verbose, but it makes it easier to know what the heck it does and my brain requires the extra help :)

These end up getting added in a pipeline by a class called the InjectionConstructorFinderBuilder. It uses a fluent interface to make things a little easier to read:

 

InjectionConstructorFinder finder =
    new InjectionConstructorFinderBuilder()
        .AddStrategy(new Find...AttributeStrategy())
        .AddStrategy(new Find...SatisfiedStrategy(_container))
        .Build();

 

I cut the names a bit because they wouldn't fit, but you get the idea. These could easily be loaded dynamically based on info in a configuration file for more extensibility.

Now when I want to find the constructor for a given type all I have to do is follows:

 

Constructor constructor = finder.Find(type);

 

Each strategy is invoked and either provides a constructor or calls the next strategy.

I find that using these types of design patterns and principles in my development makes my code much easier to understand, extend, and maintain.

Hope this helps.

 

Source: David Hayden ( Microsoft MVP C# ), Filed: Design Patterns

 

posted on Wednesday, September 19, 2007 4:21 PM

Main

News

Green Tea

.NET Development

Enterprise Library

Patterns & Practices