navigation
 Tuesday, April 15, 2008

Today I refactored some code that has been bugging me for a while, and wanted to share the results. The change resulted in a substantial code reduction and what I felt was a more elegant solution.

The code that I was looking at had a whole suite of objects that all implemented a proprietary ITransactionHandler interface, with methods such as Process(), and properties such as TransactionType and TransactionName. It was the latter two that made for some very verbose and awkward code. I found this basic pattern repeated in each class, and there were some 40 of these:

public class Tran114 : BaseTransactionHandler
{
    #region Properties

    #region TransactionName

    public override string TransactionName
    {
        get
        {
            return "Commodity Receipt (Detail)";
        }
    }

    #endregion TransactionName

    #region TransactionType

    public override int TransactionType
    {
        get 
        {
            return 114;
        }
    }

    #endregion TransactionType

    #endregion Properties

    // ...
}

Basically, each subclass of BaseTransactionHandler needed to be tagged with it's TransactionType and it's TransactionName.

Custom attributes to the rescue!

Instead, I implemented a custom attribute class (note the use of automatic properties, this is not required for custom attributes but saves some typing):

using System;

[AttributeUsage(AttributeTargets.Class)]
public class HJTransactionAttribute : Attribute
{
    public HJTransactionAttribute(string transactionName, int transactionType)
    {
        this.TransactionName = transactionName;
        this.TransactionType = transactionType;
    }

    public int TransactionType { get; set; }
    public String TransactionName { get; set; }
}

Now, I tagged each class with this new attribute, for instance:

[HJTransactionAttribute("Commodity Receipt (Detail)", 114)]
public class Tran114 : BaseTransactionHandler
{
  // ..
}

The code that used to rely on the objects implementing ITransactionHandler to access the TransactionType and TransactionName parameters changed to use these helpers that extract the attribute values from a handler instance in runtime:

public static HJTransactionAttribute GetHJTransactionAttribute(object handler)
{
    object[] attrs = handler.GetType().GetCustomAttributes(typeof(HJTransactionAttribute), false);
    if (attrs.Length == 1)
        return attrs[0] as HJTransactionAttribute;
    else
        throw new VelocityOperationalError("Transaction Handler does not have HJTransactionAttribute");
}

public static int GetTransactionType(object handler)
{
    return GetHJTransactionAttribute(handler).TransactionType;
}

public static string GetTransactionName(object handler)
{
    return GetHJTransactionAttribute(handler).TransactionName;
}

For instance:

private static void ReportError(WMS_ImportQueue record, ITransactionHandler handler)
{
  // ...
  info.Add("handler name", HJTransactionMgr.GetTransactionName(handler));
  info.Add("handler tran type", HJTransactionMgr.GetTransactionType(handler).ToString());
  // ..
}
Quite a bit more concise, wouldn't you agree?

You can read more about custom attributes here.

 | 
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, i, strike, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview