Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Generic Chain of Responsibility Builder

0.00/5 (No votes)
9 May 2010 1  
The article describes how you can implement the chain of responsility pattern with a generic chain builder method in .net

In my last project, I had several problems which I solved with the chain of responsibility pattern. It is a great pattern which allows to put something into it and the correct answer will be returned. But I don't want to explain the pattern here, more information can be found on Wiki. What is really annoying is to build the chain. If you add create a new class, which handles a new type of input, you must always remember that this new class must be added to the chain initialization. To solve this problem, the chain must be built automatically and dynamically. First, we need a common base type for all chains (this has nothing to do with the base type of the chain of responsibility pattern).

C#
namespace Common.Helper
{
  ///
  /// Base object of a responsibilty chain
  ///
  ///
  public abstract class ResponsibiltyChain<T>
  {
    protected T next;

    ///
    /// Sets the next item in the chain
    ///
    /// <param name="""""""translator""""""" />
    public T Next(T next)
    {
      this.next = next;
      return next;
    }
  }
}

This base class allows to set the next item in the chain. Every chain has to implement an abstract base type (part of the pattern) which should inherit from ResponsibiltyChain base class. With this abstract type, you can use the Chainbuilder class which is shown below. It searches for all implementations of the given type and initializes them. After that, it puts the elements of the chain in series and returns the first element. It is limited to classes which have an empty constructor (which is quite normal when you use this pattern).

C#
using System;
using System.Collections.Generic;
using System.Linq;

namespace Common.Helper
{
  /// <summary>
  /// Helper methods to create a chain of responsibility
  /// </summary>
  public class ChainBuilder
  {
    /// <summary>
    /// Loads all the classes which implements the type
    /// and builds the chain and returns the first element.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="ignore"></param>
    /// <returns></returns>
    public static T Load<T>(params Type[] ignore)
            where T: ResponsibiltyChain<T>
    {
      var tranlators = GetInstances<T>(ignore);
      var chain = (ResponsibiltyChain<T>)tranlators[0];
      var first = chain;

      for(int i = 1 ; i < tranlators.Count ; i++)
      {
        chain = (ResponsibiltyChain<T>)chain.Next(tranlators[i]);
      }

      return (T)first;
    }

    /// <summary>
    /// Instances all the types which implements the type.
    /// All types which are specified as input will be ignored
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="ignore"></param>
    /// <returns></returns>
    private static List<T> GetInstances<T>(params Type[] ignore)
    {
      var objects = new List<T>();

      foreach (var type in typeof(T).Assembly.GetTypes())
      {
        if (type.IsSubclassOf(typeof(T)) &&
           !type.IsAbstract && !ignore.Contains(type))
        {
           objects.Add( (T)Activator.CreateInstance(type));
        }
      }

      return objects;
    }
  }
}

The result is less overhead to build the chain and you will never again forget to put a new class into your chain. All this is really simple to implement which is shown in the sample below:

C#
namespace Common.Sample
{
    ///
    /// Base class of your responsibilty chain
    ///
    ///
    public abstract class MyChain : ResponsibiltyChain<MyChain>
    {
        public static MyChain Chain()
        {
            return ChainBuilder.Load<MyChain>();
        }

        public abstract void write(string message);
    }

    ///
    /// Element of the chain
    ///
    ///
    public abstract class NullChainElement : MyChain
    {
        public override void write(string message)
        {
            if (string.IsNullOrEmpty(message))
            {
                Console.WriteLine("Message was null");
            }
            else if (next != null)
            {
                next.write(message);
            }
        }
    }

    ///
    /// Element of the chain
    ///
    ///
    public abstract class NotNullChainElement : MyChain
    {
        public override void write(string message)
        {
            if (!string.IsNullOrEmpty(message))
            {
                Console.WriteLine("Message was not null");
            }
            else if (next != null)
            {
                next.write(message);
            }
        }
    }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here