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.