This question could be subjective. I’m not sure if it belongs here or on Programmers
Say I have a data type, X (think of business objects constructed from a relational database). My end goal is represent many instances of this type in a table for a report, with each instance under one of a few different headings.
The heading to display the object under is selected based on arbitrary logic handed down from management, which anybody who’s done corporate software development will be familiar with:
Examples:
If the instance has a FooID of 6 and a BarFactor of < 0.5, place it under the "Borked" heading.
or
If the Weight is > 0, and the CreatedDate is before Midnight but after 3PM, and today is not the 3rd Wednesday of the month, the object should be categorised as "Fluffy".
My question: Is there a common idiom for taking an instance of X, applying a potentially headache-inducing amount of logic to the state of the instance and getting a category/string/arbitrary value from the result of that logic?
My ideas so far have been:
-
A function which takes an
Xand returns aString. I could easily see this turning into a Megamoth, and a b***** to maintain as requirements are constantly modified. -
Define a
Headingabstract type, and a factory function which gives me an instance ofHeadingwith theToStringmethod correctly overloaded. I think this technique will be plagued by the same issues as idea no. 1. -
A hierarchy of functions, each splitting the problem up a little further until we arrive at the correct heading.
For example:
public String GetHeading(X x)
{
if (x.Weight > 0)
return WeightGreaterThanZero(x);
else if (x.Weight < 0)
return WeightLessThanZero(x);
else
return WeightIsZero(x);
}
The three “Weight” functions would test further conditions until we arrive at a value. The problem I see here is that we need to track which function called which. The FooIDIs6 function needs to know whether it was called by WeightIsZero or some other function, otherwise any preceding decisions are potentially meaningless. We end up with something like WeightIsGreaterThanZero_FooIDIs6_GrandmotherIsOlderThan100 etc. etc.
I’m not sure that this completely applies to what you are trying to accomplish, but whenever we encounter this type of “fun”, we end up providing the business users a user interface to define their rules, then just write enough code to apply the rules to the specific objects.
For example, in one of our applications, we allow the user to specify a set of conditions that, when evaluated to true, will produce a user-defined output.
To do this, we store one record in the database for each portion of the IF statement. Each record specifies the property name in the class, the comparison action (<, =, <>, etc), the comparison value, whether or not the statement begins or ends a group (i.e. parens), and how the current statement is joined with the next (AND or OR).
So you would have
We can also support user-defined priorities in the parent record so that if a given record has multiple matches, it is the user’s responsibility to determine the best match through the proper application of priorities.
At run time, you just build a statement to evaluate the conditions as entered by the user. We use reflection so that we don’t have to hard-code property names.
In addition, the same configuration code can be used to generate SQL statements in the cases where it is more appropriate to perform the selection logic in SQL.
We have used this mechanism extensively and have found it to be a very powerful approach to meeting the frequent and varied demands of our customers.