I am working on some legacy code that involves Orders placed by Customers. There is a new requirement to classify each Order by Business Unit. The Business Unit Name will be stored in a new column on the Order table.
The Order is not an actual object. I have an Order Number, which I can use to retrieve two different XML documents from a database. Well, one XML document that represents the entire Order, and one XML document that represents a Product Definition, which is retrieved using a Product ID from the Order XML Document. The Product XML documents all conform to the same schema.
I have a set of rules to apply on values obtained from the XML documents to determine the Business Unit classification.
I am trying to avoid using a lot of if/else statements because it seems messy and not very maintainable. All I need is a string
containing the Business Unit Name. The method needs to be called in two separate flows, so I have put it in a class.
Here is what the code looks like to return the Business Unit Name:
public class BusinessUnitName
{
public static string GetBusinessUnitName(string orderNumber)
{
var order = getOrder(orderNumber);
var orderType = order.SelectSingleNode("/Order/Type").InnerText;
if (orderType.Equals("Remake"))
{
var remakeReason = order.SelectSingleNode("/Order/RemakeReason").InnerText;
if (remakeReason.Equals("SHIPDMG") || remakeReason.Equals("WRNGITM"))
{
return "Shipping";
}
else
{
return "Customer Service";
}
}
else if (orderType.Equals("Design"))
{
List<string> illustrationProductTypes = new List<string> { "1204", "1205", "1206", "1207" };
List<string> customProductTypes = new List<string> { "9204", "9205", "9206", "9207" };
var firstProductId = order.SelectSingleNode("/Order/Products/Product[1]/Id").InnerText;
var firstProductDefinition = getProductDefinition(firstProductId);
var firstProductType = firstProductDefinition.SelectSingleNode("/Product/Type").InnerText;
if (illustrationProductTypes.Contains(firstProductType))
{
return "Design-Illustration";
}
else if (customProductTypes.Contains(firstProductType))
{
return "Design-Custom";
}
else
{
return "Design-Other";
}
}
else if (orderType.Equals("Samples"))
{
var firstProductId = order.SelectSingleNode("/Order/Products/Product[1]/Id").InnerText;
var firstProductDefinition = getProductDefinition(firstProductId);
var departmentCode = firstProductDefinition.SelectSingleNode("/Product/DepartmentCode").InnerText;
if (departmentCode.Equals("JWL"))
{
return "Jewelry";
}
else if (departmentCode.Equals("SPPLS"))
{
return "Craft Supplies";
}
}
}
private static XmlDocument getOrder(string orderNumber)
{
XmlDocument order = new XmlDocument();
/* execute code to retrieve XML from database
and load into order */
return order;
}
private static XmlDocument getProductDefinition(string productId)
{
XmlDocument productDefinition = new XmlDocument();
/* execute code to retrieve XML from database
and load into productDefinitionXmlDocument */
return productDefinition;
}
}
This is pretty similar to the actual code, but the list of if/else statements is about three times as long. Each rule just consists of a combination of values in XML documents.
I do not know if I am on the right path, but I was considering using the Strategy pattern. I could create an IBusinessUnitStrategy
interface with an ExecuteRules
method that returns a bool
value indicating if a combination of rules is a match, and a field indicating the Business Unit Name. I would then need to implement the IBusinessUnitStrategy for each Business Unit Name.
I would need another class that generates a list of IBusinessUnitStrategy instances for each concrete Business Unit class, then calls the ExecuteRules
method sequentially on each Business Unit class until it one of them returns true
, and then return the value of the Business Unit Name field for that class.
Would this approach make sense/be somewhat along a better path design-wise, or is it overkill?