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).
namespace Common.Helper
{
public abstract class ResponsibiltyChain<T>
{
protected T next;
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).
using System;
using System.Collections.Generic;
using System.Linq;
namespace Common.Helper
{
public class ChainBuilder
{
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;
}
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:
namespace Common.Sample
{
public abstract class MyChain : ResponsibiltyChain<MyChain>
{
public static MyChain Chain()
{
return ChainBuilder.Load<MyChain>();
}
public abstract void write(string message);
}
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);
}
}
}
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);
}
}
}
}